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.
4 
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 PLATFORM_WINDOWS && !ES_BUILD_STANDALONE && !CORECLR && !ES_BUILD_PN
8 #define FEATURE_ACTIVITYSAMPLING
9 #endif // !ES_BUILD_STANDALONE
10 
11 #if ES_BUILD_STANDALONE
12 #define FEATURE_MANAGED_ETW_CHANNELS
13 // #define FEATURE_ADVANCED_MANAGED_ETW_CHANNELS
14 #endif
15 
16 /* DESIGN NOTES DESIGN NOTES DESIGN NOTES DESIGN NOTES */
17 // DESIGN NOTES
18 // Over the years EventSource has become more complex and so it is important to understand
19 // the basic structure of the code to insure that it does not grow more complex.
20 //
21 // Basic Model
22 //
23 // PRINCIPLE: EventSource - ETW decoupling
24 //
25 // Conceptually and EventSouce is something takes event logging data from the source methods
26 // To the EventListener that can subscribe them.  Note that CONCEPTUALLY EVENTSOURCES DON'T
27 // KNOW ABOUT ETW!.   The MODEL of the system is that there is a special EventListern Which
28 // we will call the EtwEventListener, that forwards commands from ETW to EventSources and
29 // listeners to the EventSources and forwards on those events to ETW.   THus the model should
30 // be that you DON'T NEED ETW.
31 //
32 // Now in actual practice, EventSouce have rather intimate knowledge of ETW and send events
33 // to it directly, but this can be VIEWED AS AN OPTIMIATION.
34 //
35 // Basic Event Data Flow:
36 //
37 // There are two ways for event Data to enter the system
38 //     1) WriteEvent* and friends.  This is called the 'contract' based approach because
39 //        you write a method per event which forms a contract that is know at compile time.
40 //        In this scheme each event is given an EVENTID (small integer). which is its identity
41 //     2) Write<T> methods.   This is called the 'dynamic' approach because new events
42 //        can be created on the fly.  Event identity is determined by the event NAME, and these
43 //        are not quite as efficient at runtime since you have at least a hash table lookup
44 //        on every event write.
45 //
46 // EventSource-EventListener transfer fully support both ways of writing events (either contract
47 // based (WriteEvent*) or dynamic (Write<T>).   Both way fully support the same set of data
48 // types.   It is suggested, however, that you use the contract based approach when the event scheme
49 // is known at compile time (that is whenever possible).  It is more efficient, but more importantly
50 // it makes the contract very explicit, and centralizes all policy about logging.  These are good
51 // things.    The Write<T> API is really meant for more ad-hoc
52 //
53 // Allowed Data.
54 //
55 // Note that EventSource-EventListeners have a conceptual serialization-deserialization that happens
56 // during the transfer.   In particular object identity is not preserved, some objects are morphed,
57 // and not all data types are supported.   In particular you can pass
58 //
59 // A Valid type to log to an EventSource include
60 //   * Primitive data types
61 //   * IEnumerable<T> of valid types T (this include arrays)  (* New for V4.6)
62 //   * Explicitly Opted in class or struct with public property Getters over Valid types.  (* New for V4.6)
63 //
64 // This set of types is roughly a generalization of JSON support (Basically primitives, bags, and arrays).
65 //
66 // Explicitly allowed structs include (* New for V4.6)
67 //   * Marked with the EventData attribute
68 //   * implicitly defined (e.g the C# new {x = 3, y = 5} syntax)
69 //   * KeyValuePair<K,V>  (thus dictionaries can be passed since they are an IEnumerable of KeyValuePair)
70 //
71 // When classes are returned in an EventListener, what is returned is something that implements
72 // IDictionary<string, T>.  Thus when objects are passed to an EventSource they are transformed
73 // into a key-value bag (the IDictionary<string, T>) for consumption in the listener.   These
74 // are obvious NOT the original objects.
75 //
76 // ETWserialization formats:
77 //
78 // As mentioned conceptually EventSource's send data to EventListeners and there is a conceptual
79 // copy/morph of that data as described above.   In addition the .NET framework supports a conceptual
80 // ETWListener that will send the data to then ETW stream.   If you use this feature, the data needs
81 // to be serialized in a way that ETW supports.  ETW supports the following serialization formats
82 //
83 //     1) Manifest Based serialization.
84 //     2) SelfDescribing serialization (TraceLogging style in the TraceLogging directory)
85 //
86 // A key factor is that the Write<T> method, which support on the fly definition of events, can't
87 // support the manifest based serialization because the manifest needs the schema of all events
88 // to be known before any events are emitted.  This implies the following
89 //
90 // If you use Write<T> and the output goes to ETW it will use the SelfDescribing format.
91 // If you use the EventSource(string) constructor for an eventSource (in which you don't
92 // create a subclass), the default is also to use Self-Describing serialization.  In addition
93 // you can use the EventSoruce(EventSourceSettings) constructor to also explicitly specify
94 // Self-Describing serialization format.   These effect the WriteEvent* APIs going to ETW.
95 //
96 // Note that none of this ETW serialization logic affects EventListeners.   Only the ETW listener.
97 //
98 // *************************************************************************************
99 // *** INTERNALS: Event Propagation
100 //
101 //   Data enters the system either though
102 //
103 // 1) A user defined method in the user defined subclass of EventSource which calls
104 //     A) A typesafe type specific overload of WriteEvent(ID, ...)  e.g. WriteEvent(ID, string, string)
105 //           * which calls into the unsafe WriteEventCore(ID COUNT EventData*) WriteEventWithRelatedActivityIdCore()
106 //     B) The typesafe overload WriteEvent(ID, object[])  which calls the private helper WriteEventVarargs(ID, Guid* object[])
107 //     C) Directly into the unsafe WriteEventCore(ID, COUNT EventData*) or WriteEventWithRelatedActivityIdCore()
108 //
109 //     All event data eventually flows to one of
110 //        * WriteEventWithRelatedActivityIdCore(ID, Guid*, COUNT, EventData*)
111 //        * WriteEventVarargs(ID, Guid*, object[])
112 //
113 // 2) A call to one of the overloads of Write<T>.   All these overloads end up in
114 //        * WriteImpl<T>(EventName, Options, Data, Guid*, Guid*)
115 //
116 // On output there are the following routines
117 //    Writing to all listeners that are NOT ETW, we have the following routines
118 //       * WriteToAllListeners(ID, Guid*, COUNT, EventData*)
119 //       * WriteToAllListeners(ID, Guid*, object[])
120 //       * WriteToAllListeners(NAME, Guid*, EventPayload)
121 //
122 //       EventPayload is the internal type that implements the IDictionary<string, object> interface
123 //       The EventListeners will pass back for serialized classes for nested object, but
124 //       WriteToAllListeners(NAME, Guid*, EventPayload) unpacks this uses the fields as if they
125 //       were parameters to a method.
126 //
127 //       The first two are used for the WriteEvent* case, and the later is used for the Write<T> case.
128 //
129 //    Writing to ETW, Manifest Based
130 //          EventProvider.WriteEvent(EventDescriptor, Guid*, COUNT, EventData*)
131 //          EventProvider.WriteEvent(EventDescriptor, Guid*, object[])
132 //    Writing to ETW, Self-Describing format
133 //          WriteMultiMerge(NAME, Options, Types, EventData*)
134 //          WriteMultiMerge(NAME, Options, Types, object[])
135 //          WriteImpl<T> has logic that knows how to serialize (like WriteMultiMerge) but also knows
136 //             will write it to
137 //
138 //    All ETW writes eventually call
139 //      EventWriteTransfer (native PINVOKE wrapper)
140 //      EventWriteTransferWrapper (fixes compat problem if you pass null as the related activityID)
141 //         EventProvider.WriteEventRaw   - sets last error
142 //         EventSource.WriteEventRaw     - Does EventSource exception handling logic
143 //            WriteMultiMerge
144 //            WriteImpl<T>
145 //         EventProvider.WriteEvent(EventDescriptor, Guid*, COUNT, EventData*)
146 //         EventProvider.WriteEvent(EventDescriptor, Guid*, object[])
147 //
148 // Serialization:  We have a bit of a hodge-podge of serializers right now.   Only the one for ETW knows
149 // how to deal with nested classes or arrays.   I will call this serializer the 'TypeInfo' serializer
150 // since it is the TraceLoggingTypeInfo structure that knows how to do this.   Effectively for a type you
151 // can call one of these
152 //      WriteMetadata - transforms the type T into serialization meta data blob for that type
153 //      WriteObjectData - transforms an object of T into serialization meta data blob for that type
154 //      GetData - transforms an object of T into its deserialized form suitable for passing to EventListener.
155 // The first two are used to serialize something for ETW.   The second one is used to transform the object
156 // for use by the EventListener.    We also have a 'DecodeObject' method that will take a EventData* and
157 // deserialize to pass to an EventListener, but it only works on primitive types (types supported in version V4.5).
158 //
159 // It is an important observation that while EventSource does support users directly calling with EventData*
160 // blobs, we ONLY support that for the primitive types (V4.5 level support).   Thus while there is a EventData*
161 // path through the system it is only for some types.  The object[] path is the more general (but less efficient) path.
162 //
163 // TODO There is cleanup needed There should be no divergence until WriteEventRaw.
164 //
165 // TODO: We should have a single choke point (right now we always have this parallel EventData* and object[] path.   This
166 // was historical (at one point we tried to pass object directly from EventSoruce to EventListener.  That was always
167 // fragile and a compatibility headache, but we have finally been forced into the idea that there is always a transformation.
168 // This allows us to use the EventData* form to be the canonical data format in the low level APIs.  This also gives us the
169 // opportunity to expose this format to EventListeners in the future.
170 //
171 using System;
172 using System.Runtime.CompilerServices;
173 #if FEATURE_ACTIVITYSAMPLING
174 using System.Collections.Concurrent;
175 #endif
176 using System.Collections.Generic;
177 using System.Collections.ObjectModel;
178 using System.Diagnostics;
179 using System.Diagnostics.CodeAnalysis;
180 using System.Globalization;
181 using System.Reflection;
182 using System.Resources;
183 using System.Security;
184 #if !CORECLR && !ES_BUILD_PN
185 using System.Security.Permissions;
186 #endif // !CORECLR && !ES_BUILD_PN
187 
188 using System.Text;
189 using System.Threading;
190 using Microsoft.Win32;
191 
192 #if ES_BUILD_STANDALONE
193 using EventDescriptor = Microsoft.Diagnostics.Tracing.EventDescriptor;
194 #else
195 using System.Threading.Tasks;
196 #endif
197 
198 using Microsoft.Reflection;
199 
200 #if !ES_BUILD_AGAINST_DOTNET_V35
201 using Contract = System.Diagnostics.Contracts.Contract;
202 #else
203 using Contract = Microsoft.Diagnostics.Contracts.Internal.Contract;
204 #endif
205 
206 #if CORECLR || ES_BUILD_PN
207 using Internal.Runtime.Augments;
208 #endif
209 
210 #if ES_BUILD_STANDALONE
211 namespace Microsoft.Diagnostics.Tracing
212 #else
213 namespace System.Diagnostics.Tracing
214 #endif
215 {
216     /// <summary>
217     /// This class is meant to be inherited by a user-defined event source in order to define a managed
218     /// ETW provider.   Please See DESIGN NOTES above for the internal architecture.
219     /// The minimal definition of an EventSource simply specifies a number of ETW event methods that
220     /// call one of the EventSource.WriteEvent overloads, <see cref="EventSource.WriteEventCore"/>,
221     /// or <see cref="EventSource.WriteEventWithRelatedActivityIdCore"/> to log them. This functionality
222     /// is sufficient for many users.
223     /// <para>
224     /// To achieve more control over the ETW provider manifest exposed by the event source type, the
225     /// [<see cref="EventAttribute"/>] attributes can be specified for the ETW event methods.
226     /// </para><para>
227     /// For very advanced EventSources, it is possible to intercept the commands being given to the
228     /// eventSource and change what filtering is done (see EventListener.EnableEvents and
229     /// <see cref="EventListener.DisableEvents"/>) or cause actions to be performed by the eventSource,
230     /// e.g. dumping a data structure (see EventSource.SendCommand and
231     /// <see cref="EventSource.OnEventCommand"/>).
232     /// </para><para>
233     /// The eventSources can be turned on with Windows ETW controllers (e.g. logman), immediately.
234     /// It is also possible to control and intercept the data dispatcher programmatically.  See
235     /// <see cref="EventListener"/> for more.
236     /// </para>
237     /// </summary>
238     /// <remarks>
239     /// This is a minimal definition for a custom event source:
240     /// <code>
241     /// [EventSource(Name="Samples-Demos-Minimal")]
242     /// sealed class MinimalEventSource : EventSource
243     /// {
244     ///     public static MinimalEventSource Log = new MinimalEventSource();
245     ///     public void Load(long ImageBase, string Name) { WriteEvent(1, ImageBase, Name); }
246     ///     public void Unload(long ImageBase) { WriteEvent(2, ImageBase); }
247     ///     private MinimalEventSource() {}
248     /// }
249     /// </code>
250     /// </remarks>
251     public partial class EventSource : IDisposable
252     {
253 
254 #if FEATURE_EVENTSOURCE_XPLAT
255         private static readonly EventListener persistent_Xplat_Listener = XplatEventLogger.InitializePersistentListener();
256 #endif //FEATURE_EVENTSOURCE_XPLAT
257 
258         /// <summary>
259         /// The human-friendly name of the eventSource.  It defaults to the simple name of the class
260         /// </summary>
261         public string Name { get { return m_name; } }
262         /// <summary>
263         /// Every eventSource is assigned a GUID to uniquely identify it to the system.
264         /// </summary>
265         public Guid Guid { get { return m_guid; } }
266 
267         /// <summary>
268         /// Returns true if the eventSource has been enabled at all. This is the preferred test
269         /// to be performed before a relatively expensive EventSource operation.
270         /// </summary>
271         [SuppressMessage("Microsoft.Concurrency", "CA8001", Justification = "This does not need to be correct when racing with other threads")]
IsEnabled()272         public bool IsEnabled()
273         {
274             return m_eventSourceEnabled;
275         }
276 
277         /// <summary>
278         /// Returns true if events with greater than or equal 'level' and have one of 'keywords' set are enabled.
279         ///
280         /// Note that the result of this function is only an approximation on whether a particular
281         /// event is active or not. It is only meant to be used as way of avoiding expensive
282         /// computation for logging when logging is not on, therefore it sometimes returns false
283         /// positives (but is always accurate when returning false).  EventSources are free to
284         /// have additional filtering.
285         /// </summary>
286         [SuppressMessage("Microsoft.Concurrency", "CA8001", Justification = "This does not need to be correct when racing with other threads")]
IsEnabled(EventLevel level, EventKeywords keywords)287         public bool IsEnabled(EventLevel level, EventKeywords keywords)
288         {
289             return IsEnabled(level, keywords, EventChannel.None);
290         }
291 
292         /// <summary>
293         /// Returns true if events with greater than or equal 'level' and have one of 'keywords' set are enabled, or
294         /// if 'keywords' specifies a channel bit for a channel that is enabled.
295         ///
296         /// Note that the result of this function only an approximation on whether a particular
297         /// event is active or not. It is only meant to be used as way of avoiding expensive
298         /// computation for logging when logging is not on, therefore it sometimes returns false
299         /// positives (but is always accurate when returning false).  EventSources are free to
300         /// have additional filtering.
301         /// </summary>
302         [SuppressMessage("Microsoft.Concurrency", "CA8001", Justification = "This does not need to be correct when racing with other threads")]
IsEnabled(EventLevel level, EventKeywords keywords, EventChannel channel)303         public bool IsEnabled(EventLevel level, EventKeywords keywords, EventChannel channel)
304         {
305             if (!m_eventSourceEnabled)
306                 return false;
307 
308             if (!IsEnabledCommon(m_eventSourceEnabled, m_level, m_matchAnyKeyword, level, keywords, channel))
309                 return false;
310 
311 #if !FEATURE_ACTIVITYSAMPLING
312 
313             return true;
314 
315 #else // FEATURE_ACTIVITYSAMPLING
316 
317             return true;
318 
319 #if OPTIMIZE_IS_ENABLED
320             //================================================================================
321             // 2013/03/06 - The code below is a possible optimization for IsEnabled(level, kwd)
322             //    in case activity tracing/sampling is enabled. The added complexity of this
323             //    code however weighs against having it "on" until we know it's really needed.
324             //    For now we'll have this #ifdef-ed out in case we see evidence this is needed.
325             //================================================================================
326 
327             // At this point we believe the event is enabled, however we now need to check
328             // if we filter because of activity
329 
330             // Optimization, all activity filters also register a delegate here, so if there
331             // is no delegate, we know there are no activity filters, which means that there
332             // is no additional filtering, which means that we can return true immediately.
333             if (s_activityDying == null)
334                 return true;
335 
336             // if there's at least one legacy ETW listener we can't filter this
337             if (m_legacySessions != null && m_legacySessions.Count > 0)
338                 return true;
339 
340             // if any event ID that triggers a new activity, or "transfers" activities
341             // is covered by 'keywords' we can't filter this
342             if (unchecked(((long)keywords & m_keywordTriggers)) != 0)
343                 return true;
344 
345             // See if all listeners have activity filters that would block the event.
346             for (int perEventSourceSessionId = 0; perEventSourceSessionId < SessionMask.MAX; ++perEventSourceSessionId)
347             {
348                 EtwSession etwSession = m_etwSessionIdMap[perEventSourceSessionId];
349                 if (etwSession == null)
350                     continue;
351 
352                 ActivityFilter activityFilter = etwSession.m_activityFilter;
353                 if (activityFilter == null ||
354                     ActivityFilter.GetFilter(activityFilter, this) == null)
355                 {
356                     // No activity filter for ETW, if event is active for ETW, we can't filter.
357                     for (int i = 0; i < m_eventData.Length; i++)
358                         if (m_eventData[i].EnabledForETW)
359                             return true;
360                 }
361                 else if (ActivityFilter.IsCurrentActivityActive(activityFilter))
362                     return true;
363             }
364 
365             // for regular event listeners
366             var curDispatcher = m_Dispatchers;
367             while (curDispatcher != null)
368             {
369                 ActivityFilter activityFilter = curDispatcher.m_Listener.m_activityFilter;
370                 if (activityFilter == null)
371                 {
372                     // See if any event is enabled.
373                     for (int i = 0; i < curDispatcher.m_EventEnabled.Length; i++)
374                         if (curDispatcher.m_EventEnabled[i])
375                             return true;
376                 }
377                 else if (ActivityFilter.IsCurrentActivityActive(activityFilter))
378                     return true;
379                 curDispatcher = curDispatcher.m_Next;
380             }
381 
382             // Every listener has an activity filter that is blocking writing the event,
383             // thus the event is not enabled.
384             return false;
385 #endif // OPTIMIZE_IS_ENABLED
386 
387 #endif // FEATURE_ACTIVITYSAMPLING
388         }
389 
390         /// <summary>
391         /// Returns the settings for the event source instance
392         /// </summary>
393         public EventSourceSettings Settings
394         {
395             get { return m_config; }
396         }
397 
398         // Manifest support
399         /// <summary>
400         /// Returns the GUID that uniquely identifies the eventSource defined by 'eventSourceType'.
401         /// This API allows you to compute this without actually creating an instance of the EventSource.
402         /// It only needs to reflect over the type.
403         /// </summary>
GetGuid(Type eventSourceType)404         public static Guid GetGuid(Type eventSourceType)
405         {
406             if (eventSourceType == null)
407                 throw new ArgumentNullException(nameof(eventSourceType));
408 
409             EventSourceAttribute attrib = (EventSourceAttribute)GetCustomAttributeHelper(eventSourceType, typeof(EventSourceAttribute));
410             string name = eventSourceType.Name;
411             if (attrib != null)
412             {
413                 if (attrib.Guid != null)
414                 {
415                     Guid g = Guid.Empty;
416 #if !ES_BUILD_AGAINST_DOTNET_V35
417                     if (Guid.TryParse(attrib.Guid, out g))
418                         return g;
419 #else
420                     try { return new Guid(attrib.Guid); }
421                     catch (Exception) { }
422 #endif
423                 }
424 
425                 if (attrib.Name != null)
426                     name = attrib.Name;
427             }
428 
429             if (name == null)
430             {
431                 throw new ArgumentException(SR.Argument_InvalidTypeName, nameof(eventSourceType));
432             }
433             return GenerateGuidFromName(name.ToUpperInvariant());       // Make it case insensitive.
434         }
435         /// <summary>
436         /// Returns the official ETW Provider name for the eventSource defined by 'eventSourceType'.
437         /// This API allows you to compute this without actually creating an instance of the EventSource.
438         /// It only needs to reflect over the type.
439         /// </summary>
GetName(Type eventSourceType)440         public static string GetName(Type eventSourceType)
441         {
442             return GetName(eventSourceType, EventManifestOptions.None);
443         }
444 
445         /// <summary>
446         /// Returns a string of the XML manifest associated with the eventSourceType. The scheme for this XML is
447         /// documented at in EventManifest Schema http://msdn.microsoft.com/en-us/library/aa384043(VS.85).aspx.
448         /// This is the preferred way of generating a manifest to be embedded in the ETW stream as it is fast and
449         /// the fact that it only includes localized entries for the current UI culture is an acceptable tradeoff.
450         /// </summary>
451         /// <param name="eventSourceType">The type of the event source class for which the manifest is generated</param>
452         /// <param name="assemblyPathToIncludeInManifest">The manifest XML fragment contains the string name of the DLL name in
453         /// which it is embedded.  This parameter specifies what name will be used</param>
454         /// <returns>The XML data string</returns>
GenerateManifest(Type eventSourceType, string assemblyPathToIncludeInManifest)455         public static string GenerateManifest(Type eventSourceType, string assemblyPathToIncludeInManifest)
456         {
457             return GenerateManifest(eventSourceType, assemblyPathToIncludeInManifest, EventManifestOptions.None);
458         }
459         /// <summary>
460         /// Returns a string of the XML manifest associated with the eventSourceType. The scheme for this XML is
461         /// documented at in EventManifest Schema http://msdn.microsoft.com/en-us/library/aa384043(VS.85).aspx.
462         /// Pass EventManifestOptions.AllCultures when generating a manifest to be registered on the machine. This
463         /// ensures that the entries in the event log will be "optimally" localized.
464         /// </summary>
465         /// <param name="eventSourceType">The type of the event source class for which the manifest is generated</param>
466         /// <param name="assemblyPathToIncludeInManifest">The manifest XML fragment contains the string name of the DLL name in
467         /// which it is embedded.  This parameter specifies what name will be used</param>
468         /// <param name="flags">The flags to customize manifest generation. If flags has bit OnlyIfNeededForRegistration specified
469         /// this returns null when the eventSourceType does not require explicit registration</param>
470         /// <returns>The XML data string or null</returns>
GenerateManifest(Type eventSourceType, string assemblyPathToIncludeInManifest, EventManifestOptions flags)471         public static string GenerateManifest(Type eventSourceType, string assemblyPathToIncludeInManifest, EventManifestOptions flags)
472         {
473             if (eventSourceType == null)
474                 throw new ArgumentNullException(nameof(eventSourceType));
475 
476             byte[] manifestBytes = EventSource.CreateManifestAndDescriptors(eventSourceType, assemblyPathToIncludeInManifest, null, flags);
477             return (manifestBytes == null) ? null : Encoding.UTF8.GetString(manifestBytes, 0, manifestBytes.Length);
478         }
479 
480         // EventListener support
481         /// <summary>
482         /// returns a list (IEnumerable) of all sources in the appdomain).  EventListeners typically need this.
483         /// </summary>
484         /// <returns></returns>
GetSources()485         public static IEnumerable<EventSource> GetSources()
486         {
487             var ret = new List<EventSource>();
488             lock (EventListener.EventListenersLock)
489             {
490                 foreach (WeakReference eventSourceRef in EventListener.s_EventSources)
491                 {
492                     EventSource eventSource = eventSourceRef.Target as EventSource;
493                     if (eventSource != null && !eventSource.IsDisposed)
494                         ret.Add(eventSource);
495                 }
496             }
497             return ret;
498         }
499 
500         /// <summary>
501         /// Send a command to a particular EventSource identified by 'eventSource'.
502         /// Calling this routine simply forwards the command to the EventSource.OnEventCommand
503         /// callback.  What the EventSource does with the command and its arguments are from
504         /// that point EventSource-specific.
505         /// </summary>
506         /// <param name="eventSource">The instance of EventSource to send the command to</param>
507         /// <param name="command">A positive user-defined EventCommand, or EventCommand.SendManifest</param>
508         /// <param name="commandArguments">A set of (name-argument, value-argument) pairs associated with the command</param>
SendCommand(EventSource eventSource, EventCommand command, IDictionary<string, string> commandArguments)509         public static void SendCommand(EventSource eventSource, EventCommand command, IDictionary<string, string> commandArguments)
510         {
511             if (eventSource == null)
512                 throw new ArgumentNullException(nameof(eventSource));
513 
514             // User-defined EventCommands should not conflict with the reserved commands.
515             if ((int)command <= (int)EventCommand.Update && (int)command != (int)EventCommand.SendManifest)
516             {
517                 throw new ArgumentException(SR.EventSource_InvalidCommand, nameof(command));
518             }
519 
520             eventSource.SendCommand(null, 0, 0, command, true, EventLevel.LogAlways, EventKeywords.None, commandArguments);
521         }
522 
523 #if !ES_BUILD_STANDALONE
524         /// <summary>
525         /// This property allows EventSource code to appropriately handle as "different"
526         /// activities started on different threads that have not had an activity created on them.
527         /// </summary>
528         internal static Guid InternalCurrentThreadActivityId
529         {
530             get
531             {
532                 Guid retval = CurrentThreadActivityId;
533                 if (retval == Guid.Empty)
534                 {
535                     retval = FallbackActivityId;
536                 }
537                 return retval;
538             }
539         }
540 
541         internal static Guid FallbackActivityId
542         {
543             get
544             {
545 #pragma warning disable 612, 618
546                 int threadID = AppDomain.GetCurrentThreadId();
547 
548                 // Managed thread IDs are more aggressively re-used than native thread IDs,
549                 // so we'll use the latter...
550                 return new Guid(unchecked((uint)threadID),
551                                 unchecked((ushort)s_currentPid), unchecked((ushort)(s_currentPid >> 16)),
552                                 0x94, 0x1b, 0x87, 0xd5, 0xa6, 0x5c, 0x36, 0x64);
553 #pragma warning restore 612, 618
554             }
555         }
556 #endif // !ES_BUILD_STANDALONE
557 
558         // Error APIs.  (We don't throw by default, but you can probe for status)
559         /// <summary>
560         /// Because
561         ///
562         ///     1) Logging is often optional and thus should not generate fatal errors (exceptions)
563         ///     2) EventSources are often initialized in class constructors (which propagate exceptions poorly)
564         ///
565         /// The event source constructor does not throw exceptions.  Instead we remember any exception that
566         /// was generated (it is also logged to Trace.WriteLine).
567         /// </summary>
568         public Exception ConstructionException { get { return m_constructionException; } }
569 
570         /// <summary>
571         /// EventSources can have arbitrary string key-value pairs associated with them called Traits.
572         /// These traits are not interpreted by the EventSource but may be interpreted by EventListeners
573         /// (e.g. like the built in ETW listener).   These traits are specified at EventSource
574         /// construction time and can be retrieved by using this GetTrait API.
575         /// </summary>
576         /// <param name="key">The key to look up in the set of key-value pairs passed to the EventSource constructor</param>
577         /// <returns>The value string associated with key.  Will return null if there is no such key.</returns>
GetTrait(string key)578         public string GetTrait(string key)
579         {
580             if (m_traits != null)
581             {
582                 for (int i = 0; i < m_traits.Length - 1; i += 2)
583                 {
584                     if (m_traits[i] == key)
585                         return m_traits[i + 1];
586                 }
587             }
588             return null;
589         }
590 
591         /// <summary>
592         /// Displays the name and GUID for the eventSource for debugging purposes.
593         /// </summary>
ToString()594         public override string ToString()
595         {
596             return SR.Format(SR.EventSource_ToString, Name, Guid);
597         }
598 
599         /// <summary>
600         /// Fires when a Command (e.g. Enable) comes from a an EventListener.
601         /// </summary>
602         public event EventHandler<EventCommandEventArgs> EventCommandExecuted
603         {
604             add
605             {
606                 m_eventCommandExecuted += value;
607 
608                 // If we have an EventHandler<EventCommandEventArgs> attached to the EventSource before the first command arrives
609                 // It should get a chance to handle the deferred commands.
610                 EventCommandEventArgs deferredCommands = m_deferredCommands;
611                 while (deferredCommands != null)
612                 {
613                     value(this, deferredCommands);
614                     deferredCommands = deferredCommands.nextCommand;
615                 }
616             }
617             remove
618             {
619                 m_eventCommandExecuted -= value;
620             }
621         }
622 
623         #region protected
624         /// <summary>
625         /// This is the constructor that most users will use to create their eventSource.   It takes
626         /// no parameters.  The ETW provider name and GUID of the EventSource are determined by the EventSource
627         /// custom attribute (so you can determine these things declaratively).   If the GUID for the eventSource
628         /// is not specified in the EventSourceAttribute (recommended), it is Generated by hashing the name.
629         /// If the ETW provider name of the EventSource is not given, the name of the EventSource class is used as
630         /// the ETW provider name.
631         /// </summary>
EventSource()632         protected EventSource()
633             : this(EventSourceSettings.EtwManifestEventFormat)
634         {
635         }
636 
637         /// <summary>
638         /// By default calling the 'WriteEvent' methods do NOT throw on errors (they silently discard the event).
639         /// This is because in most cases users assume logging is not 'precious' and do NOT wish to have logging failures
640         /// crash the program. However for those applications where logging is 'precious' and if it fails the caller
641         /// wishes to react, setting 'throwOnEventWriteErrors' will cause an exception to be thrown if WriteEvent
642         /// fails. Note the fact that EventWrite succeeds does not necessarily mean that the event reached its destination
643         /// only that operation of writing it did not fail. These EventSources will not generate self-describing ETW events.
644         ///
645         /// For compatibility only use the EventSourceSettings.ThrowOnEventWriteErrors flag instead.
646         /// </summary>
647         // [Obsolete("Use the EventSource(EventSourceSettings) overload")]
EventSource(bool throwOnEventWriteErrors)648         protected EventSource(bool throwOnEventWriteErrors)
649             : this(EventSourceSettings.EtwManifestEventFormat | (throwOnEventWriteErrors ? EventSourceSettings.ThrowOnEventWriteErrors : 0))
650         { }
651 
652         /// <summary>
653         /// Construct an EventSource with additional non-default settings (see EventSourceSettings for more)
654         /// </summary>
EventSource(EventSourceSettings settings)655         protected EventSource(EventSourceSettings settings) : this(settings, null) { }
656 
657         /// <summary>
658         /// Construct an EventSource with additional non-default settings.
659         ///
660         /// Also specify a list of key-value pairs called traits (you must pass an even number of strings).
661         /// The first string is the key and the second is the value.   These are not interpreted by EventSource
662         /// itself but may be interpreted the listeners.  Can be fetched with GetTrait(string).
663         /// </summary>
664         /// <param name="settings">See EventSourceSettings for more.</param>
665         /// <param name="traits">A collection of key-value strings (must be an even number).</param>
EventSource(EventSourceSettings settings, params string[] traits)666         protected EventSource(EventSourceSettings settings, params string[] traits)
667         {
668             m_config = ValidateSettings(settings);
669 
670             Guid eventSourceGuid;
671             string eventSourceName;
672 
673             EventMetadata[] eventDescriptors;
674             byte[] manifest;
675             GetMetadata(out eventSourceGuid, out eventSourceName, out eventDescriptors, out manifest);
676 
677             if (eventSourceGuid.Equals(Guid.Empty) || eventSourceName == null)
678             {
679                 var myType = this.GetType();
680                 eventSourceGuid = GetGuid(myType);
681                 eventSourceName = GetName(myType);
682             }
683 
684             Initialize(eventSourceGuid, eventSourceName, traits);
685         }
686 
687 #if FEATURE_PERFTRACING
688         // Generate the serialized blobs that describe events for all strongly typed events (that is events that define strongly
689         // typed event methods. Dynamically defined events (that use Write) hare defined on the fly and are handled elsewhere.
DefineEventPipeEvents()690         private unsafe void DefineEventPipeEvents()
691         {
692             Debug.Assert(m_eventData != null);
693             Debug.Assert(m_provider != null);
694             int cnt = m_eventData.Length;
695             for (int i = 0; i < cnt; i++)
696             {
697                 uint eventID = (uint)m_eventData[i].Descriptor.EventId;
698                 if (eventID == 0)
699                     continue;
700 
701                 string eventName = m_eventData[i].Name;
702                 Int64 keywords = m_eventData[i].Descriptor.Keywords;
703                 uint eventVersion = m_eventData[i].Descriptor.Version;
704                 uint level = m_eventData[i].Descriptor.Level;
705 
706                 // evnetID          : 4 bytes
707                 // eventName        : (eventName.Length + 1) * 2 bytes
708                 // keywords         : 8 bytes
709                 // eventVersion     : 4 bytes
710                 // level            : 4 bytes
711                 // parameterCount   : 4 bytes
712                 uint metadataLength = 24 + ((uint)eventName.Length + 1) * 2;
713 
714                 // Increase the metadataLength for the types of all parameters.
715                 metadataLength += (uint)m_eventData[i].Parameters.Length * 4;
716 
717                 // Increase the metadataLength for the names of all parameters.
718                 foreach (var parameter in m_eventData[i].Parameters)
719                 {
720                     string parameterName = parameter.Name;
721                     metadataLength = metadataLength + ((uint)parameterName.Length + 1) * 2;
722                 }
723 
724                 byte[] metadata = new byte[metadataLength];
725 
726                 // Write metadata: evnetID, eventName, keywords, eventVersion, level, parameterCount, param1 type, param1 name...
727                 fixed (byte *pMetadata = metadata)
728                 {
729                     uint offset = 0;
730                     WriteToBuffer(pMetadata, metadataLength, ref offset, eventID);
731                     fixed(char *pEventName = eventName)
732                     {
733                         WriteToBuffer(pMetadata, metadataLength, ref offset, (byte *)pEventName, ((uint)eventName.Length + 1) * 2);
734                     }
735                     WriteToBuffer(pMetadata, metadataLength, ref offset, keywords);
736                     WriteToBuffer(pMetadata, metadataLength, ref offset, eventVersion);
737                     WriteToBuffer(pMetadata, metadataLength, ref offset, level);
738                     WriteToBuffer(pMetadata, metadataLength, ref offset, (uint)m_eventData[i].Parameters.Length);
739                     foreach (var parameter in m_eventData[i].Parameters)
740                     {
741                         // Write parameter type.
742                         WriteToBuffer(pMetadata, metadataLength, ref offset, (uint)Type.GetTypeCode(parameter.ParameterType));
743 
744                         // Write parameter name.
745                         string parameterName = parameter.Name;
746                         fixed (char *pParameterName = parameterName)
747                         {
748                             WriteToBuffer(pMetadata, metadataLength, ref offset, (byte *)pParameterName, ((uint)parameterName.Length + 1) * 2);
749                         }
750                     }
751                     Debug.Assert(metadataLength == offset);
752                     IntPtr eventHandle = m_provider.m_eventProvider.DefineEventHandle(eventID, eventName, keywords, eventVersion, level, pMetadata, metadataLength);
753                     m_eventData[i].EventHandle = eventHandle;
754                 }
755             }
756         }
757 
758         // Copy src to buffer and modify the offset.
759         // Note: We know the buffer size ahead of time to make sure no buffer overflow.
WriteToBuffer(byte *buffer, uint bufferLength, ref uint offset, byte *src, uint srcLength)760         private static unsafe void WriteToBuffer(byte *buffer, uint bufferLength, ref uint offset, byte *src, uint srcLength)
761         {
762             Debug.Assert(bufferLength >= (offset + srcLength));
763             for (int i = 0; i < srcLength; i++)
764             {
765                 *(byte *)(buffer + offset + i) = *(byte *)(src + i);
766             }
767             offset += srcLength;
768         }
769 
770         // Copy uint value to buffer.
WriteToBuffer(byte *buffer, uint bufferLength, ref uint offset, uint value)771         private static unsafe void WriteToBuffer(byte *buffer, uint bufferLength, ref uint offset, uint value)
772         {
773             Debug.Assert(bufferLength >= (offset + 4));
774             *(uint *)(buffer + offset) = value;
775             offset += 4;
776         }
777 
778         // Copy long value to buffer.
WriteToBuffer(byte *buffer, uint bufferLength, ref uint offset, long value)779         private static unsafe void WriteToBuffer(byte *buffer, uint bufferLength, ref uint offset, long value)
780         {
781             Debug.Assert(bufferLength >= (offset + 8));
782             *(long *)(buffer + offset) = value;
783             offset += 8;
784         }
785 #endif
786 
GetMetadata(out Guid eventSourceGuid, out string eventSourceName, out EventMetadata[] eventData, out byte[] manifestBytes)787         internal virtual void GetMetadata(out Guid eventSourceGuid, out string eventSourceName, out EventMetadata[] eventData, out byte[] manifestBytes)
788         {
789             //
790             // In ProjectN subclasses need to override this method, and return the data from their EventSourceAttribute and EventAttribute annotations.
791             // On other architectures it is a no-op.
792             //
793             // eventDescriptors needs to contain one EventDescriptor for each event; the event's ID should be the same as its index in this array.
794             // manifestBytes is a UTF-8 encoding of the ETW manifest for the type.
795             //
796             // 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.
797             //
798             eventSourceGuid = Guid.Empty;
799             eventSourceName = null;
800             eventData = null;
801             manifestBytes = null;
802 
803             return;
804         }
805 
806         /// <summary>
807         /// This method is called when the eventSource is updated by the controller.
808         /// </summary>
OnEventCommand(EventCommandEventArgs command)809         protected virtual void OnEventCommand(EventCommandEventArgs command) { }
810 
811 #pragma warning disable 1591
812         // optimized for common signatures (no args)
813         [SuppressMessage("Microsoft.Concurrency", "CA8001", Justification = "This does not need to be correct when racing with other threads")]
WriteEvent(int eventId)814         protected unsafe void WriteEvent(int eventId)
815         {
816             WriteEventCore(eventId, 0, null);
817         }
818 
819         // optimized for common signatures (ints)
820         [SuppressMessage("Microsoft.Concurrency", "CA8001", Justification = "This does not need to be correct when racing with other threads")]
WriteEvent(int eventId, int arg1)821         protected unsafe void WriteEvent(int eventId, int arg1)
822         {
823             if (m_eventSourceEnabled)
824             {
825                 EventSource.EventData* descrs = stackalloc EventSource.EventData[1];
826                 descrs[0].DataPointer = (IntPtr)(&arg1);
827                 descrs[0].Size = 4;
828                 WriteEventCore(eventId, 1, descrs);
829             }
830         }
831 
832         [SuppressMessage("Microsoft.Concurrency", "CA8001", Justification = "This does not need to be correct when racing with other threads")]
WriteEvent(int eventId, int arg1, int arg2)833         protected unsafe void WriteEvent(int eventId, int arg1, int arg2)
834         {
835             if (m_eventSourceEnabled)
836             {
837                 EventSource.EventData* descrs = stackalloc EventSource.EventData[2];
838                 descrs[0].DataPointer = (IntPtr)(&arg1);
839                 descrs[0].Size = 4;
840                 descrs[1].DataPointer = (IntPtr)(&arg2);
841                 descrs[1].Size = 4;
842                 WriteEventCore(eventId, 2, descrs);
843             }
844         }
845 
846         [SuppressMessage("Microsoft.Concurrency", "CA8001", Justification = "This does not need to be correct when racing with other threads")]
WriteEvent(int eventId, int arg1, int arg2, int arg3)847         protected unsafe void WriteEvent(int eventId, int arg1, int arg2, int arg3)
848         {
849             if (m_eventSourceEnabled)
850             {
851                 EventSource.EventData* descrs = stackalloc EventSource.EventData[3];
852                 descrs[0].DataPointer = (IntPtr)(&arg1);
853                 descrs[0].Size = 4;
854                 descrs[1].DataPointer = (IntPtr)(&arg2);
855                 descrs[1].Size = 4;
856                 descrs[2].DataPointer = (IntPtr)(&arg3);
857                 descrs[2].Size = 4;
858                 WriteEventCore(eventId, 3, descrs);
859             }
860         }
861 
862         // optimized for common signatures (longs)
863         [SuppressMessage("Microsoft.Concurrency", "CA8001", Justification = "This does not need to be correct when racing with other threads")]
WriteEvent(int eventId, long arg1)864         protected unsafe void WriteEvent(int eventId, long arg1)
865         {
866             if (m_eventSourceEnabled)
867             {
868                 EventSource.EventData* descrs = stackalloc EventSource.EventData[1];
869                 descrs[0].DataPointer = (IntPtr)(&arg1);
870                 descrs[0].Size = 8;
871                 WriteEventCore(eventId, 1, descrs);
872             }
873         }
874 
875         [SuppressMessage("Microsoft.Concurrency", "CA8001", Justification = "This does not need to be correct when racing with other threads")]
WriteEvent(int eventId, long arg1, long arg2)876         protected unsafe void WriteEvent(int eventId, long arg1, long arg2)
877         {
878             if (m_eventSourceEnabled)
879             {
880                 EventSource.EventData* descrs = stackalloc EventSource.EventData[2];
881                 descrs[0].DataPointer = (IntPtr)(&arg1);
882                 descrs[0].Size = 8;
883                 descrs[1].DataPointer = (IntPtr)(&arg2);
884                 descrs[1].Size = 8;
885                 WriteEventCore(eventId, 2, descrs);
886             }
887         }
888 
889         [SuppressMessage("Microsoft.Concurrency", "CA8001", Justification = "This does not need to be correct when racing with other threads")]
WriteEvent(int eventId, long arg1, long arg2, long arg3)890         protected unsafe void WriteEvent(int eventId, long arg1, long arg2, long arg3)
891         {
892             if (m_eventSourceEnabled)
893             {
894                 EventSource.EventData* descrs = stackalloc EventSource.EventData[3];
895                 descrs[0].DataPointer = (IntPtr)(&arg1);
896                 descrs[0].Size = 8;
897                 descrs[1].DataPointer = (IntPtr)(&arg2);
898                 descrs[1].Size = 8;
899                 descrs[2].DataPointer = (IntPtr)(&arg3);
900                 descrs[2].Size = 8;
901                 WriteEventCore(eventId, 3, descrs);
902             }
903         }
904 
905         // optimized for common signatures (strings)
906         [SuppressMessage("Microsoft.Concurrency", "CA8001", Justification = "This does not need to be correct when racing with other threads")]
WriteEvent(int eventId, string arg1)907         protected unsafe void WriteEvent(int eventId, string arg1)
908         {
909             if (m_eventSourceEnabled)
910             {
911                 if (arg1 == null) arg1 = "";
912                 fixed (char* string1Bytes = arg1)
913                 {
914                     EventSource.EventData* descrs = stackalloc EventSource.EventData[1];
915                     descrs[0].DataPointer = (IntPtr)string1Bytes;
916                     descrs[0].Size = ((arg1.Length + 1) * 2);
917                     WriteEventCore(eventId, 1, descrs);
918                 }
919             }
920         }
921 
922         [SuppressMessage("Microsoft.Concurrency", "CA8001", Justification = "This does not need to be correct when racing with other threads")]
WriteEvent(int eventId, string arg1, string arg2)923         protected unsafe void WriteEvent(int eventId, string arg1, string arg2)
924         {
925             if (m_eventSourceEnabled)
926             {
927                 if (arg1 == null) arg1 = "";
928                 if (arg2 == null) arg2 = "";
929                 fixed (char* string1Bytes = arg1)
930                 fixed (char* string2Bytes = arg2)
931                 {
932                     EventSource.EventData* descrs = stackalloc EventSource.EventData[2];
933                     descrs[0].DataPointer = (IntPtr)string1Bytes;
934                     descrs[0].Size = ((arg1.Length + 1) * 2);
935                     descrs[1].DataPointer = (IntPtr)string2Bytes;
936                     descrs[1].Size = ((arg2.Length + 1) * 2);
937                     WriteEventCore(eventId, 2, descrs);
938                 }
939             }
940         }
941 
942         [SuppressMessage("Microsoft.Concurrency", "CA8001", Justification = "This does not need to be correct when racing with other threads")]
WriteEvent(int eventId, string arg1, string arg2, string arg3)943         protected unsafe void WriteEvent(int eventId, string arg1, string arg2, string arg3)
944         {
945             if (m_eventSourceEnabled)
946             {
947                 if (arg1 == null) arg1 = "";
948                 if (arg2 == null) arg2 = "";
949                 if (arg3 == null) arg3 = "";
950                 fixed (char* string1Bytes = arg1)
951                 fixed (char* string2Bytes = arg2)
952                 fixed (char* string3Bytes = arg3)
953                 {
954                     EventSource.EventData* descrs = stackalloc EventSource.EventData[3];
955                     descrs[0].DataPointer = (IntPtr)string1Bytes;
956                     descrs[0].Size = ((arg1.Length + 1) * 2);
957                     descrs[1].DataPointer = (IntPtr)string2Bytes;
958                     descrs[1].Size = ((arg2.Length + 1) * 2);
959                     descrs[2].DataPointer = (IntPtr)string3Bytes;
960                     descrs[2].Size = ((arg3.Length + 1) * 2);
961                     WriteEventCore(eventId, 3, descrs);
962                 }
963             }
964         }
965 
966         // optimized for common signatures (string and ints)
967         [SuppressMessage("Microsoft.Concurrency", "CA8001", Justification = "This does not need to be correct when racing with other threads")]
WriteEvent(int eventId, string arg1, int arg2)968         protected unsafe void WriteEvent(int eventId, string arg1, int arg2)
969         {
970             if (m_eventSourceEnabled)
971             {
972                 if (arg1 == null) arg1 = "";
973                 fixed (char* string1Bytes = arg1)
974                 {
975                     EventSource.EventData* descrs = stackalloc EventSource.EventData[2];
976                     descrs[0].DataPointer = (IntPtr)string1Bytes;
977                     descrs[0].Size = ((arg1.Length + 1) * 2);
978                     descrs[1].DataPointer = (IntPtr)(&arg2);
979                     descrs[1].Size = 4;
980                     WriteEventCore(eventId, 2, descrs);
981                 }
982             }
983         }
984 
985         [SuppressMessage("Microsoft.Concurrency", "CA8001", Justification = "This does not need to be correct when racing with other threads")]
WriteEvent(int eventId, string arg1, int arg2, int arg3)986         protected unsafe void WriteEvent(int eventId, string arg1, int arg2, int arg3)
987         {
988             if (m_eventSourceEnabled)
989             {
990                 if (arg1 == null) arg1 = "";
991                 fixed (char* string1Bytes = arg1)
992                 {
993                     EventSource.EventData* descrs = stackalloc EventSource.EventData[3];
994                     descrs[0].DataPointer = (IntPtr)string1Bytes;
995                     descrs[0].Size = ((arg1.Length + 1) * 2);
996                     descrs[1].DataPointer = (IntPtr)(&arg2);
997                     descrs[1].Size = 4;
998                     descrs[2].DataPointer = (IntPtr)(&arg3);
999                     descrs[2].Size = 4;
1000                     WriteEventCore(eventId, 3, descrs);
1001                 }
1002             }
1003         }
1004 
1005         // optimized for common signatures (string and longs)
1006         [SuppressMessage("Microsoft.Concurrency", "CA8001", Justification = "This does not need to be correct when racing with other threads")]
WriteEvent(int eventId, string arg1, long arg2)1007         protected unsafe void WriteEvent(int eventId, string arg1, long arg2)
1008         {
1009             if (m_eventSourceEnabled)
1010             {
1011                 if (arg1 == null) arg1 = "";
1012                 fixed (char* string1Bytes = arg1)
1013                 {
1014                     EventSource.EventData* descrs = stackalloc EventSource.EventData[2];
1015                     descrs[0].DataPointer = (IntPtr)string1Bytes;
1016                     descrs[0].Size = ((arg1.Length + 1) * 2);
1017                     descrs[1].DataPointer = (IntPtr)(&arg2);
1018                     descrs[1].Size = 8;
1019                     WriteEventCore(eventId, 2, descrs);
1020                 }
1021             }
1022         }
1023 
1024         // optimized for common signatures (long and string)
1025         [SuppressMessage("Microsoft.Concurrency", "CA8001", Justification = "This does not need to be correct when racing with other threads")]
WriteEvent(int eventId, long arg1, string arg2)1026         protected unsafe void WriteEvent(int eventId, long arg1, string arg2)
1027         {
1028             if (m_eventSourceEnabled)
1029             {
1030                 if (arg2 == null) arg2 = "";
1031                 fixed (char* string2Bytes = arg2)
1032                 {
1033                     EventSource.EventData* descrs = stackalloc EventSource.EventData[2];
1034                     descrs[0].DataPointer = (IntPtr)(&arg1);
1035                     descrs[0].Size = 8;
1036                     descrs[1].DataPointer = (IntPtr)string2Bytes;
1037                     descrs[1].Size = ((arg2.Length + 1) * 2);
1038                     WriteEventCore(eventId, 2, descrs);
1039                 }
1040             }
1041         }
1042 
1043         // optimized for common signatures (int and string)
1044         [SuppressMessage("Microsoft.Concurrency", "CA8001", Justification = "This does not need to be correct when racing with other threads")]
WriteEvent(int eventId, int arg1, string arg2)1045         protected unsafe void WriteEvent(int eventId, int arg1, string arg2)
1046         {
1047             if (m_eventSourceEnabled)
1048             {
1049                 if (arg2 == null) arg2 = "";
1050                 fixed (char* string2Bytes = arg2)
1051                 {
1052                     EventSource.EventData* descrs = stackalloc EventSource.EventData[2];
1053                     descrs[0].DataPointer = (IntPtr)(&arg1);
1054                     descrs[0].Size = 4;
1055                     descrs[1].DataPointer = (IntPtr)string2Bytes;
1056                     descrs[1].Size = ((arg2.Length + 1) * 2);
1057                     WriteEventCore(eventId, 2, descrs);
1058                 }
1059             }
1060         }
1061 
1062         [SuppressMessage("Microsoft.Concurrency", "CA8001", Justification = "This does not need to be correct when racing with other threads")]
WriteEvent(int eventId, byte[] arg1)1063         protected unsafe void WriteEvent(int eventId, byte[] arg1)
1064         {
1065             if (m_eventSourceEnabled)
1066             {
1067                 EventSource.EventData* descrs = stackalloc EventSource.EventData[2];
1068                 if (arg1 == null || arg1.Length == 0)
1069                 {
1070                     int blobSize = 0;
1071                     descrs[0].DataPointer = (IntPtr)(&blobSize);
1072                     descrs[0].Size = 4;
1073                     descrs[1].DataPointer = (IntPtr)(&blobSize); // valid address instead of empty content
1074                     descrs[1].Size = 0;
1075                     WriteEventCore(eventId, 2, descrs);
1076                 }
1077                 else
1078                 {
1079                     int blobSize = arg1.Length;
1080                     fixed (byte* blob = &arg1[0])
1081                     {
1082                         descrs[0].DataPointer = (IntPtr)(&blobSize);
1083                         descrs[0].Size = 4;
1084                         descrs[1].DataPointer = (IntPtr)blob;
1085                         descrs[1].Size = blobSize;
1086                         WriteEventCore(eventId, 2, descrs);
1087                     }
1088                 }
1089             }
1090         }
1091 
1092         [SuppressMessage("Microsoft.Concurrency", "CA8001", Justification = "This does not need to be correct when racing with other threads")]
WriteEvent(int eventId, long arg1, byte[] arg2)1093         protected unsafe void WriteEvent(int eventId, long arg1, byte[] arg2)
1094         {
1095             if (m_eventSourceEnabled)
1096             {
1097                 EventSource.EventData* descrs = stackalloc EventSource.EventData[3];
1098                 descrs[0].DataPointer = (IntPtr)(&arg1);
1099                 descrs[0].Size = 8;
1100                 if (arg2 == null || arg2.Length == 0)
1101                 {
1102                     int blobSize = 0;
1103                     descrs[1].DataPointer = (IntPtr)(&blobSize);
1104                     descrs[1].Size = 4;
1105                     descrs[2].DataPointer = (IntPtr)(&blobSize); // valid address instead of empty contents
1106                     descrs[2].Size = 0;
1107                     WriteEventCore(eventId, 3, descrs);
1108                 }
1109                 else
1110                 {
1111                     int blobSize = arg2.Length;
1112                     fixed (byte* blob = &arg2[0])
1113                     {
1114                         descrs[1].DataPointer = (IntPtr)(&blobSize);
1115                         descrs[1].Size = 4;
1116                         descrs[2].DataPointer = (IntPtr)blob;
1117                         descrs[2].Size = blobSize;
1118                         WriteEventCore(eventId, 3, descrs);
1119                     }
1120                 }
1121             }
1122         }
1123 
1124 #pragma warning restore 1591
1125 
1126         /// <summary>
1127         /// Used to construct the data structure to be passed to the native ETW APIs - EventWrite and EventWriteTransfer.
1128         /// </summary>
1129         protected internal struct EventData
1130         {
1131             /// <summary>
1132             /// Address where the one argument lives (if this points to managed memory you must ensure the
1133             /// managed object is pinned.
1134             /// </summary>
1135             public unsafe IntPtr DataPointer { get { return (IntPtr)(void*)m_Ptr; } set { m_Ptr = unchecked((ulong)(void*)value); } }
1136 
1137             /// <summary>
1138             /// Size of the argument referenced by DataPointer
1139             /// </summary>
1140             public int Size { get { return m_Size; } set { m_Size = value; } }
1141 
1142             #region private
1143             /// <summary>
1144             /// Initializes the members of this EventData object to point at a previously-pinned
1145             /// tracelogging-compatible metadata blob.
1146             /// </summary>
1147             /// <param name="pointer">Pinned tracelogging-compatible metadata blob.</param>
1148             /// <param name="size">The size of the metadata blob.</param>
1149             /// <param name="reserved">Value for reserved: 2 for per-provider metadata, 1 for per-event metadata</param>
SetMetadataMicrosoft.Diagnostics.Tracing.EventSource.EventData1150             internal unsafe void SetMetadata(byte* pointer, int size, int reserved)
1151             {
1152                 this.m_Ptr = (ulong)pointer;
1153                 this.m_Size = size;
1154                 this.m_Reserved = reserved; // Mark this descriptor as containing tracelogging-compatible metadata.
1155             }
1156 
1157             //Important, we pass this structure directly to the Win32 EventWrite API, so this structure must be layed out exactly
1158             // the way EventWrite wants it.
1159             internal ulong m_Ptr;
1160             internal int m_Size;
1161 #pragma warning disable 0649
1162             internal int m_Reserved;       // Used to pad the size to match the Win32 API
1163 #pragma warning restore 0649
1164             #endregion
1165         }
1166 
1167         /// <summary>
1168         /// This routine allows you to create efficient WriteEvent helpers, however the code that you use to
1169         /// do this, while straightforward, is unsafe.
1170         /// </summary>
1171         /// <remarks>
1172         /// <code>
1173         ///    protected unsafe void WriteEvent(int eventId, string arg1, long arg2)
1174         ///    {
1175         ///        if (IsEnabled())
1176         ///        {
1177         ///            if (arg2 == null) arg2 = "";
1178         ///            fixed (char* string2Bytes = arg2)
1179         ///            {
1180         ///                EventSource.EventData* descrs = stackalloc EventSource.EventData[2];
1181         ///                descrs[0].DataPointer = (IntPtr)(&amp;arg1);
1182         ///                descrs[0].Size = 8;
1183         ///                descrs[1].DataPointer = (IntPtr)string2Bytes;
1184         ///                descrs[1].Size = ((arg2.Length + 1) * 2);
1185         ///                WriteEventCore(eventId, 2, descrs);
1186         ///            }
1187         ///        }
1188         ///    }
1189         /// </code>
1190         /// </remarks>
1191         [CLSCompliant(false)]
WriteEventCore(int eventId, int eventDataCount, EventSource.EventData* data)1192         protected unsafe void WriteEventCore(int eventId, int eventDataCount, EventSource.EventData* data)
1193         {
1194             WriteEventWithRelatedActivityIdCore(eventId, null, eventDataCount, data);
1195         }
1196 
1197         /// <summary>
1198         /// This routine allows you to create efficient WriteEventWithRelatedActivityId helpers, however the code
1199         /// that you use to do this, while straightforward, is unsafe. The only difference from
1200         /// <see cref="WriteEventCore"/> is that you pass the relatedActivityId from caller through to this API
1201         /// </summary>
1202         /// <remarks>
1203         /// <code>
1204         ///    protected unsafe void WriteEventWithRelatedActivityId(int eventId, Guid relatedActivityId, string arg1, long arg2)
1205         ///    {
1206         ///        if (IsEnabled())
1207         ///        {
1208         ///            if (arg2 == null) arg2 = "";
1209         ///            fixed (char* string2Bytes = arg2)
1210         ///            {
1211         ///                EventSource.EventData* descrs = stackalloc EventSource.EventData[2];
1212         ///                descrs[0].DataPointer = (IntPtr)(&amp;arg1);
1213         ///                descrs[0].Size = 8;
1214         ///                descrs[1].DataPointer = (IntPtr)string2Bytes;
1215         ///                descrs[1].Size = ((arg2.Length + 1) * 2);
1216         ///                WriteEventWithRelatedActivityIdCore(eventId, relatedActivityId, 2, descrs);
1217         ///            }
1218         ///        }
1219         ///    }
1220         /// </code>
1221         /// </remarks>
1222         [CLSCompliant(false)]
WriteEventWithRelatedActivityIdCore(int eventId, Guid* relatedActivityId, int eventDataCount, EventSource.EventData* data)1223         protected unsafe void WriteEventWithRelatedActivityIdCore(int eventId, Guid* relatedActivityId, int eventDataCount, EventSource.EventData* data)
1224         {
1225             if (m_eventSourceEnabled)
1226             {
1227                 try
1228                 {
1229                     Debug.Assert(m_eventData != null);  // You must have initialized this if you enabled the source.
1230                     if (relatedActivityId != null)
1231                         ValidateEventOpcodeForTransfer(ref m_eventData[eventId], m_eventData[eventId].Name);
1232 
1233                     EventOpcode opcode = (EventOpcode)m_eventData[eventId].Descriptor.Opcode;
1234                     EventActivityOptions activityOptions = m_eventData[eventId].ActivityOptions;
1235                     Guid* pActivityId = null;
1236                     Guid activityId = Guid.Empty;
1237                     Guid relActivityId = Guid.Empty;
1238 
1239                     if (opcode != EventOpcode.Info && relatedActivityId == null &&
1240                        ((activityOptions & EventActivityOptions.Disable) == 0))
1241                     {
1242                         if (opcode == EventOpcode.Start)
1243                         {
1244                             m_activityTracker.OnStart(m_name, m_eventData[eventId].Name, m_eventData[eventId].Descriptor.Task, ref activityId, ref relActivityId, m_eventData[eventId].ActivityOptions);
1245                         }
1246                         else if (opcode == EventOpcode.Stop)
1247                         {
1248                             m_activityTracker.OnStop(m_name, m_eventData[eventId].Name, m_eventData[eventId].Descriptor.Task, ref activityId);
1249                         }
1250 
1251                         if (activityId != Guid.Empty)
1252                             pActivityId = &activityId;
1253                         if (relActivityId != Guid.Empty)
1254                             relatedActivityId = &relActivityId;
1255                     }
1256 
1257 #if FEATURE_MANAGED_ETW
1258                     if (m_eventData[eventId].EnabledForETW)
1259                     {
1260 
1261 #if FEATURE_ACTIVITYSAMPLING
1262                         // this code should be kept in sync with WriteEventVarargs().
1263                         SessionMask etwSessions = SessionMask.All;
1264                         // only compute etwSessions if there are *any* ETW filters enabled...
1265                         if ((ulong)m_curLiveSessions != 0)
1266                             etwSessions = GetEtwSessionMask(eventId, relatedActivityId);
1267                         // OutputDebugString(string.Format("{0}.WriteEvent(id {1}) -> to sessions {2:x}",
1268                         //                   m_name, m_eventData[eventId].Name, (ulong) etwSessions));
1269 
1270                         if ((ulong)etwSessions != 0 || m_legacySessions != null && m_legacySessions.Count > 0)
1271                         {
1272                             if (!SelfDescribingEvents)
1273                             {
1274                                 if (etwSessions.IsEqualOrSupersetOf(m_curLiveSessions))
1275                                 {
1276                                     // OutputDebugString(string.Format("  (1) id {0}, kwd {1:x}",
1277                                     //                   m_eventData[eventId].Name, m_eventData[eventId].Descriptor.Keywords));
1278                                     // by default the Descriptor.Keyword will have the perEventSourceSessionId bit
1279                                     // mask set to 0x0f so, when all ETW sessions want the event we don't need to
1280                                     // synthesize a new one
1281                                     if (!m_provider.WriteEvent(ref m_eventData[eventId].Descriptor, m_eventData[eventId].EventHandle, pActivityId, relatedActivityId, eventDataCount, (IntPtr)data))
1282                                         ThrowEventSourceException(m_eventData[eventId].Name);
1283                                 }
1284                                 else
1285                                 {
1286                                     long origKwd = unchecked((long)((ulong)m_eventData[eventId].Descriptor.Keywords & ~(SessionMask.All.ToEventKeywords())));
1287                                     // OutputDebugString(string.Format("  (2) id {0}, kwd {1:x}",
1288                                     //                   m_eventData[eventId].Name, etwSessions.ToEventKeywords() | (ulong) origKwd));
1289                                     // only some of the ETW sessions will receive this event. Synthesize a new
1290                                     // Descriptor whose Keywords field will have the appropriate bits set.
1291                                     // etwSessions might be 0, if there are legacy ETW listeners that want this event
1292                                     var desc = new EventDescriptor(
1293                                         m_eventData[eventId].Descriptor.EventId,
1294                                         m_eventData[eventId].Descriptor.Version,
1295                                         m_eventData[eventId].Descriptor.Channel,
1296                                         m_eventData[eventId].Descriptor.Level,
1297                                         m_eventData[eventId].Descriptor.Opcode,
1298                                         m_eventData[eventId].Descriptor.Task,
1299                                         unchecked((long)etwSessions.ToEventKeywords() | origKwd));
1300 
1301                                     if (!m_provider.WriteEvent(ref desc, m_eventData[eventId].EventHandle, pActivityId, relatedActivityId, eventDataCount, (IntPtr)data))
1302                                         ThrowEventSourceException(m_eventData[eventId].Name);
1303                                 }
1304                             }
1305                             else
1306                             {
1307                                 TraceLoggingEventTypes tlet = m_eventData[eventId].TraceLoggingEventTypes;
1308                                 if (tlet == null)
1309                                 {
1310                                     tlet = new TraceLoggingEventTypes(m_eventData[eventId].Name,
1311                                                                             EventTags.None,
1312                                                                         m_eventData[eventId].Parameters);
1313                                     Interlocked.CompareExchange(ref m_eventData[eventId].TraceLoggingEventTypes, tlet, null);
1314 
1315                                 }
1316                                 long origKwd = unchecked((long)((ulong)m_eventData[eventId].Descriptor.Keywords & ~(SessionMask.All.ToEventKeywords())));
1317                                 // TODO: activity ID support
1318                                 EventSourceOptions opt = new EventSourceOptions
1319                                 {
1320                                     Keywords = (EventKeywords)unchecked((long)etwSessions.ToEventKeywords() | origKwd),
1321                                     Level = (EventLevel)m_eventData[eventId].Descriptor.Level,
1322                                     Opcode = (EventOpcode)m_eventData[eventId].Descriptor.Opcode
1323                                 };
1324 
1325                                 WriteMultiMerge(m_eventData[eventId].Name, ref opt, tlet, pActivityId, relatedActivityId, data);
1326                             }
1327                         }
1328 #else
1329                         if (!SelfDescribingEvents)
1330                         {
1331                             if (!m_provider.WriteEvent(ref m_eventData[eventId].Descriptor, m_eventData[eventId].EventHandle, pActivityId, relatedActivityId, eventDataCount, (IntPtr)data))
1332                                 ThrowEventSourceException(m_eventData[eventId].Name);
1333                         }
1334                         else
1335                         {
1336                             TraceLoggingEventTypes tlet = m_eventData[eventId].TraceLoggingEventTypes;
1337                             if (tlet == null)
1338                             {
1339                                 tlet = new TraceLoggingEventTypes(m_eventData[eventId].Name,
1340                                                                     m_eventData[eventId].Tags,
1341                                                                     m_eventData[eventId].Parameters);
1342                                 Interlocked.CompareExchange(ref m_eventData[eventId].TraceLoggingEventTypes, tlet, null);
1343 
1344                             }
1345                             EventSourceOptions opt = new EventSourceOptions
1346                             {
1347                                 Keywords = (EventKeywords)m_eventData[eventId].Descriptor.Keywords,
1348                                 Level = (EventLevel)m_eventData[eventId].Descriptor.Level,
1349                                 Opcode = (EventOpcode)m_eventData[eventId].Descriptor.Opcode
1350                             };
1351 
1352                             WriteMultiMerge(m_eventData[eventId].Name, ref opt, tlet, pActivityId, relatedActivityId, data);
1353                         }
1354 #endif // FEATURE_ACTIVITYSAMPLING
1355                     }
1356 #endif // FEATURE_MANAGED_ETW
1357 
1358                     if (m_Dispatchers != null && m_eventData[eventId].EnabledForAnyListener)
1359                         WriteToAllListeners(eventId, relatedActivityId, eventDataCount, data);
1360                 }
1361                 catch (Exception ex)
1362                 {
1363                     if (ex is EventSourceException)
1364                         throw;
1365                     else
1366                         ThrowEventSourceException(m_eventData[eventId].Name, ex);
1367                 }
1368             }
1369         }
1370 
1371         // fallback varags helpers.
1372         /// <summary>
1373         /// This is the varargs helper for writing an event. It does create an array and box all the arguments so it is
1374         /// relatively inefficient and should only be used for relatively rare events (e.g. less than 100 / sec). If your
1375         /// rates are faster than that you should use <see cref="WriteEventCore"/> to create fast helpers for your particular
1376         /// method signature. Even if you use this for rare events, this call should be guarded by an <see cref="IsEnabled()"/>
1377         /// check so that the varargs call is not made when the EventSource is not active.
1378         /// </summary>
1379         [SuppressMessage("Microsoft.Concurrency", "CA8001", Justification = "This does not need to be correct when racing with other threads")]
WriteEvent(int eventId, params object[] args)1380         protected unsafe void WriteEvent(int eventId, params object[] args)
1381         {
1382             WriteEventVarargs(eventId, null, args);
1383         }
1384 
1385         /// <summary>
1386         /// This is the varargs helper for writing an event which also specifies a related activity. It is completely analogous
1387         /// to corresponding WriteEvent (they share implementation). It does create an array and box all the arguments so it is
1388         /// relatively inefficient and should only be used for relatively rare events (e.g. less than 100 / sec).  If your
1389         /// rates are faster than that you should use <see cref="WriteEventWithRelatedActivityIdCore"/> to create fast helpers for your
1390         /// particular method signature. Even if you use this for rare events, this call should be guarded by an <see cref="IsEnabled()"/>
1391         /// check so that the varargs call is not made when the EventSource is not active.
1392         /// </summary>
WriteEventWithRelatedActivityId(int eventId, Guid relatedActivityId, params object[] args)1393         protected unsafe void WriteEventWithRelatedActivityId(int eventId, Guid relatedActivityId, params object[] args)
1394         {
1395             WriteEventVarargs(eventId, &relatedActivityId, args);
1396         }
1397 
1398         #endregion
1399 
1400         #region IDisposable Members
1401         /// <summary>
1402         /// Disposes of an EventSource.
1403         /// </summary>
Dispose()1404         public void Dispose()
1405         {
1406             this.Dispose(true);
1407             GC.SuppressFinalize(this);
1408         }
1409         /// <summary>
1410         /// Disposes of an EventSource.
1411         /// </summary>
1412         /// <remarks>
1413         /// Called from Dispose() with disposing=true, and from the finalizer (~EventSource) with disposing=false.
1414         /// Guidelines:
1415         /// 1. We may be called more than once: do nothing after the first call.
1416         /// 2. Avoid throwing exceptions if disposing is false, i.e. if we're being finalized.
1417         /// </remarks>
1418         /// <param name="disposing">True if called from Dispose(), false if called from the finalizer.</param>
Dispose(bool disposing)1419         protected virtual void Dispose(bool disposing)
1420         {
1421             if (disposing)
1422             {
1423 #if FEATURE_MANAGED_ETW
1424 #if !FEATURE_PERFTRACING
1425                 // Send the manifest one more time to ensure circular buffers have a chance to get to this information
1426                 // even in scenarios with a high volume of ETW events.
1427                 if (m_eventSourceEnabled)
1428                 {
1429                     try
1430                     {
1431                         SendManifest(m_rawManifest);
1432                     }
1433                     catch (Exception)
1434                     { }           // If it fails, simply give up.
1435                     m_eventSourceEnabled = false;
1436                 }
1437 #endif
1438                 if (m_provider != null)
1439                 {
1440                     m_provider.Dispose();
1441                     m_provider = null;
1442                 }
1443 #endif
1444             }
1445             m_eventSourceEnabled = false;
1446             m_eventSourceDisposed = true;
1447         }
1448         /// <summary>
1449         /// Finalizer for EventSource
1450         /// </summary>
~EventSource()1451         ~EventSource()
1452         {
1453             this.Dispose(false);
1454         }
1455         #endregion
1456 
1457         #region private
1458 #if FEATURE_ACTIVITYSAMPLING
WriteStringToListener(EventListener listener, string msg, SessionMask m)1459         internal void WriteStringToListener(EventListener listener, string msg, SessionMask m)
1460         {
1461             Debug.Assert(listener == null || (uint)m == (uint)SessionMask.FromId(0));
1462 
1463             if (m_eventSourceEnabled)
1464             {
1465                 if (listener == null)
1466                 {
1467                     WriteEventString(0, unchecked((long)m.ToEventKeywords()), msg);
1468                 }
1469                 else
1470                 {
1471                     EventWrittenEventArgs eventCallbackArgs = new EventWrittenEventArgs(this);
1472                     eventCallbackArgs.EventId = 0;
1473                     eventCallbackArgs.Message = msg;
1474                     eventCallbackArgs.Payload = new ReadOnlyCollection<object>(new List<object>() { msg });
1475                     eventCallbackArgs.PayloadNames = new ReadOnlyCollection<string>(new List<string> { "message" });
1476                     eventCallbackArgs.EventName = "EventSourceMessage";
1477                     listener.OnEventWritten(eventCallbackArgs);
1478                 }
1479             }
1480         }
1481 #endif
1482 
WriteEventRaw( string eventName, ref EventDescriptor eventDescriptor, Guid* activityID, Guid* relatedActivityID, int dataCount, IntPtr data)1483         private unsafe void WriteEventRaw(
1484             string eventName,
1485             ref EventDescriptor eventDescriptor,
1486             Guid* activityID,
1487             Guid* relatedActivityID,
1488             int dataCount,
1489             IntPtr data)
1490         {
1491 #if FEATURE_MANAGED_ETW
1492             if (m_provider == null)
1493             {
1494                 ThrowEventSourceException(eventName);
1495             }
1496             else
1497             {
1498                 if (!m_provider.WriteEventRaw(ref eventDescriptor, activityID, relatedActivityID, dataCount, data))
1499                     ThrowEventSourceException(eventName);
1500             }
1501 #endif // FEATURE_MANAGED_ETW
1502         }
1503 
1504         // FrameworkEventSource is on the startup path for the framework, so we have this internal overload that it can use
1505         // to prevent the working set hit from looking at the custom attributes on the type to get the Guid.
EventSource(Guid eventSourceGuid, string eventSourceName)1506         internal EventSource(Guid eventSourceGuid, string eventSourceName)
1507             : this(eventSourceGuid, eventSourceName, EventSourceSettings.EtwManifestEventFormat)
1508         { }
1509 
1510         // Used by the internal FrameworkEventSource constructor and the TraceLogging-style event source constructor
EventSource(Guid eventSourceGuid, string eventSourceName, EventSourceSettings settings, string[] traits = null)1511         internal EventSource(Guid eventSourceGuid, string eventSourceName, EventSourceSettings settings, string[] traits = null)
1512         {
1513             m_config = ValidateSettings(settings);
1514             Initialize(eventSourceGuid, eventSourceName, traits);
1515         }
1516 
1517         /// <summary>
1518         /// This method is responsible for the common initialization path from our constructors. It must
1519         /// not leak any exceptions (otherwise, since most EventSource classes define a static member,
1520         /// "Log", such an exception would become a cached exception for the initialization of the static
1521         /// member, and any future access to the "Log" would throw the cached exception).
1522         /// </summary>
1523         [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1720:IdentifiersShouldNotContainTypeNames", MessageId = "guid")]
Initialize(Guid eventSourceGuid, string eventSourceName, string[] traits)1524         private unsafe void Initialize(Guid eventSourceGuid, string eventSourceName, string[] traits)
1525         {
1526             try
1527             {
1528                 m_traits = traits;
1529                 if (m_traits != null && m_traits.Length % 2 != 0)
1530                 {
1531                     throw new ArgumentException(SR.EventSource_TraitEven, nameof(traits));
1532                 }
1533 
1534                 if (eventSourceGuid == Guid.Empty)
1535                 {
1536                     throw new ArgumentException(SR.EventSource_NeedGuid);
1537                 }
1538 
1539                 if (eventSourceName == null)
1540                 {
1541                     throw new ArgumentException(SR.EventSource_NeedName);
1542                 }
1543 
1544                 m_name = eventSourceName;
1545                 m_guid = eventSourceGuid;
1546 #if FEATURE_ACTIVITYSAMPLING
1547                 m_curLiveSessions = new SessionMask(0);
1548                 m_etwSessionIdMap = new EtwSession[SessionMask.MAX];
1549 #endif // FEATURE_ACTIVITYSAMPLING
1550 
1551                 //Enable Implicit Activity tracker
1552                 m_activityTracker = ActivityTracker.Instance;
1553 
1554 #if FEATURE_MANAGED_ETW
1555                 // Create and register our provider traits.  We do this early because it is needed to log errors
1556                 // In the self-describing event case.
1557                 this.InitializeProviderMetadata();
1558 
1559                 // Register the provider with ETW
1560                 var provider = new OverideEventProvider(this);
1561                 provider.Register(this);
1562 #endif
1563                 // Add the eventSource to the global (weak) list.
1564                 // This also sets m_id, which is the index in the list.
1565                 EventListener.AddEventSource(this);
1566 
1567 #if FEATURE_MANAGED_ETW
1568                 // OK if we get this far without an exception, then we can at least write out error messages.
1569                 // Set m_provider, which allows this.
1570                 m_provider = provider;
1571 
1572 #if PLATFORM_WINDOWS
1573 #if (!ES_BUILD_STANDALONE && !ES_BUILD_PN)
1574                 // API available on OS >= Win 8 and patched Win 7.
1575                 // Disable only for FrameworkEventSource to avoid recursion inside exception handling.
1576                 if (this.Name != "System.Diagnostics.Eventing.FrameworkEventSource" || Environment.IsWindows8OrAbove)
1577 #endif
1578                 {
1579                     int setInformationResult;
1580                     System.Runtime.InteropServices.GCHandle metadataHandle =
1581                         System.Runtime.InteropServices.GCHandle.Alloc(this.providerMetadata, System.Runtime.InteropServices.GCHandleType.Pinned);
1582                     IntPtr providerMetadata = metadataHandle.AddrOfPinnedObject();
1583 
1584                     setInformationResult = m_provider.SetInformation(
1585                         UnsafeNativeMethods.ManifestEtw.EVENT_INFO_CLASS.SetTraits,
1586                         providerMetadata,
1587                         (uint)this.providerMetadata.Length);
1588 
1589                     metadataHandle.Free();
1590                 }
1591 #endif // PLATFORM_WINDOWS
1592 #endif // FEATURE_MANAGED_ETW
1593                 Debug.Assert(!m_eventSourceEnabled);     // We can't be enabled until we are completely initted.
1594                 // We are logically completely initialized at this point.
1595                 m_completelyInited = true;
1596             }
1597             catch (Exception e)
1598             {
1599                 if (m_constructionException == null)
1600                     m_constructionException = e;
1601                 ReportOutOfBandMessage("ERROR: Exception during construction of EventSource " + Name + ": " + e.Message, true);
1602             }
1603 
1604             // Once m_completelyInited is set, you can have concurrency, so all work is under the lock.
1605             lock (EventListener.EventListenersLock)
1606             {
1607                 // If there are any deferred commands, we can do them now.
1608                 // This is the most likely place for exceptions to happen.
1609                 // Note that we are NOT resetting m_deferredCommands to NULL here,
1610                 // We are giving for EventHandler<EventCommandEventArgs> that will be attached later
1611                 EventCommandEventArgs deferredCommands = m_deferredCommands;
1612                 while (deferredCommands != null)
1613                 {
1614                     DoCommand(deferredCommands);      // This can never throw, it catches them and reports the errors.
1615                     deferredCommands = deferredCommands.nextCommand;
1616                 }
1617             }
1618         }
1619 
GetName(Type eventSourceType, EventManifestOptions flags)1620         private static string GetName(Type eventSourceType, EventManifestOptions flags)
1621         {
1622             if (eventSourceType == null)
1623                 throw new ArgumentNullException(nameof(eventSourceType));
1624 
1625             EventSourceAttribute attrib = (EventSourceAttribute)GetCustomAttributeHelper(eventSourceType, typeof(EventSourceAttribute), flags);
1626             if (attrib != null && attrib.Name != null)
1627                 return attrib.Name;
1628 
1629             return eventSourceType.Name;
1630         }
1631 
1632         /// <summary>
1633         /// Implements the SHA1 hashing algorithm. Note that this
1634         /// implementation is for hashing public information. Do not
1635         /// use this code to hash private data, as this implementation does
1636         /// not take any steps to avoid information disclosure.
1637         /// </summary>
1638         private struct Sha1ForNonSecretPurposes
1639         {
1640             private long length; // Total message length in bits
1641             private uint[] w; // Workspace
1642             private int pos; // Length of current chunk in bytes
1643 
1644             /// <summary>
1645             /// Call Start() to initialize the hash object.
1646             /// </summary>
StartMicrosoft.Diagnostics.Tracing.EventSource.Sha1ForNonSecretPurposes1647             public void Start()
1648             {
1649                 if (this.w == null)
1650                 {
1651                     this.w = new uint[85];
1652                 }
1653 
1654                 this.length = 0;
1655                 this.pos = 0;
1656                 this.w[80] = 0x67452301;
1657                 this.w[81] = 0xEFCDAB89;
1658                 this.w[82] = 0x98BADCFE;
1659                 this.w[83] = 0x10325476;
1660                 this.w[84] = 0xC3D2E1F0;
1661             }
1662 
1663             /// <summary>
1664             /// Adds an input byte to the hash.
1665             /// </summary>
1666             /// <param name="input">Data to include in the hash.</param>
AppendMicrosoft.Diagnostics.Tracing.EventSource.Sha1ForNonSecretPurposes1667             public void Append(byte input)
1668             {
1669                 this.w[this.pos / 4] = (this.w[this.pos / 4] << 8) | input;
1670                 if (64 == ++this.pos)
1671                 {
1672                     this.Drain();
1673                 }
1674             }
1675 
1676             /// <summary>
1677             /// Adds input bytes to the hash.
1678             /// </summary>
1679             /// <param name="input">
1680             /// Data to include in the hash. Must not be null.
1681             /// </param>
AppendMicrosoft.Diagnostics.Tracing.EventSource.Sha1ForNonSecretPurposes1682             public void Append(byte[] input)
1683             {
1684                 foreach (var b in input)
1685                 {
1686                     this.Append(b);
1687                 }
1688             }
1689 
1690             /// <summary>
1691             /// Retrieves the hash value.
1692             /// Note that after calling this function, the hash object should
1693             /// be considered uninitialized. Subsequent calls to Append or
1694             /// Finish will produce useless results. Call Start() to
1695             /// reinitialize.
1696             /// </summary>
1697             /// <param name="output">
1698             /// Buffer to receive the hash value. Must not be null.
1699             /// Up to 20 bytes of hash will be written to the output buffer.
1700             /// If the buffer is smaller than 20 bytes, the remaining hash
1701             /// bytes will be lost. If the buffer is larger than 20 bytes, the
1702             /// rest of the buffer is left unmodified.
1703             /// </param>
FinishMicrosoft.Diagnostics.Tracing.EventSource.Sha1ForNonSecretPurposes1704             public void Finish(byte[] output)
1705             {
1706                 long l = this.length + 8 * this.pos;
1707                 this.Append(0x80);
1708                 while (this.pos != 56)
1709                 {
1710                     this.Append(0x00);
1711                 }
1712 
1713                 unchecked
1714                 {
1715                     this.Append((byte)(l >> 56));
1716                     this.Append((byte)(l >> 48));
1717                     this.Append((byte)(l >> 40));
1718                     this.Append((byte)(l >> 32));
1719                     this.Append((byte)(l >> 24));
1720                     this.Append((byte)(l >> 16));
1721                     this.Append((byte)(l >> 8));
1722                     this.Append((byte)l);
1723 
1724                     int end = output.Length < 20 ? output.Length : 20;
1725                     for (int i = 0; i != end; i++)
1726                     {
1727                         uint temp = this.w[80 + i / 4];
1728                         output[i] = (byte)(temp >> 24);
1729                         this.w[80 + i / 4] = temp << 8;
1730                     }
1731                 }
1732             }
1733 
1734             /// <summary>
1735             /// Called when this.pos reaches 64.
1736             /// </summary>
DrainMicrosoft.Diagnostics.Tracing.EventSource.Sha1ForNonSecretPurposes1737             private void Drain()
1738             {
1739                 for (int i = 16; i != 80; i++)
1740                 {
1741                     this.w[i] = Rol1((this.w[i - 3] ^ this.w[i - 8] ^ this.w[i - 14] ^ this.w[i - 16]));
1742                 }
1743 
1744                 unchecked
1745                 {
1746                     uint a = this.w[80];
1747                     uint b = this.w[81];
1748                     uint c = this.w[82];
1749                     uint d = this.w[83];
1750                     uint e = this.w[84];
1751 
1752                     for (int i = 0; i != 20; i++)
1753                     {
1754                         const uint k = 0x5A827999;
1755                         uint f = (b & c) | ((~b) & d);
1756                         uint temp = Rol5(a) + f + e + k + this.w[i]; e = d; d = c; c = Rol30(b); b = a; a = temp;
1757                     }
1758 
1759                     for (int i = 20; i != 40; i++)
1760                     {
1761                         uint f = b ^ c ^ d;
1762                         const uint k = 0x6ED9EBA1;
1763                         uint temp = Rol5(a) + f + e + k + this.w[i]; e = d; d = c; c = Rol30(b); b = a; a = temp;
1764                     }
1765 
1766                     for (int i = 40; i != 60; i++)
1767                     {
1768                         uint f = (b & c) | (b & d) | (c & d);
1769                         const uint k = 0x8F1BBCDC;
1770                         uint temp = Rol5(a) + f + e + k + this.w[i]; e = d; d = c; c = Rol30(b); b = a; a = temp;
1771                     }
1772 
1773                     for (int i = 60; i != 80; i++)
1774                     {
1775                         uint f = b ^ c ^ d;
1776                         const uint k = 0xCA62C1D6;
1777                         uint temp = Rol5(a) + f + e + k + this.w[i]; e = d; d = c; c = Rol30(b); b = a; a = temp;
1778                     }
1779 
1780                     this.w[80] += a;
1781                     this.w[81] += b;
1782                     this.w[82] += c;
1783                     this.w[83] += d;
1784                     this.w[84] += e;
1785                 }
1786 
1787                 this.length += 512; // 64 bytes == 512 bits
1788                 this.pos = 0;
1789             }
1790 
Rol1Microsoft.Diagnostics.Tracing.EventSource.Sha1ForNonSecretPurposes1791             private static uint Rol1(uint input)
1792             {
1793                 return (input << 1) | (input >> 31);
1794             }
1795 
Rol5Microsoft.Diagnostics.Tracing.EventSource.Sha1ForNonSecretPurposes1796             private static uint Rol5(uint input)
1797             {
1798                 return (input << 5) | (input >> 27);
1799             }
1800 
Rol30Microsoft.Diagnostics.Tracing.EventSource.Sha1ForNonSecretPurposes1801             private static uint Rol30(uint input)
1802             {
1803                 return (input << 30) | (input >> 2);
1804             }
1805         }
1806 
GenerateGuidFromName(string name)1807         private static Guid GenerateGuidFromName(string name)
1808         {
1809             byte[] bytes = Encoding.BigEndianUnicode.GetBytes(name);
1810             var hash = new Sha1ForNonSecretPurposes();
1811             hash.Start();
1812             hash.Append(namespaceBytes);
1813             hash.Append(bytes);
1814             Array.Resize(ref bytes, 16);
1815             hash.Finish(bytes);
1816 
1817             bytes[7] = unchecked((byte)((bytes[7] & 0x0F) | 0x50));    // Set high 4 bits of octet 7 to 5, as per RFC 4122
1818             return new Guid(bytes);
1819         }
1820 
DecodeObject(int eventId, int parameterId, ref EventSource.EventData* data)1821         private unsafe object DecodeObject(int eventId, int parameterId, ref EventSource.EventData* data)
1822         {
1823             // TODO FIX : We use reflection which in turn uses EventSource, right now we carefully avoid
1824             // the recursion, but can we do this in a robust way?
1825 
1826             IntPtr dataPointer = data->DataPointer;
1827             // advance to next EventData in array
1828             ++data;
1829 
1830             Type dataType = GetDataType(m_eventData[eventId], parameterId);
1831 
1832             Again:
1833             if (dataType == typeof(IntPtr))
1834             {
1835                 return *((IntPtr*)dataPointer);
1836             }
1837             else if (dataType == typeof(int))
1838             {
1839                 return *((int*)dataPointer);
1840             }
1841             else if (dataType == typeof(uint))
1842             {
1843                 return *((uint*)dataPointer);
1844             }
1845             else if (dataType == typeof(long))
1846             {
1847                 return *((long*)dataPointer);
1848             }
1849             else if (dataType == typeof(ulong))
1850             {
1851                 return *((ulong*)dataPointer);
1852             }
1853             else if (dataType == typeof(byte))
1854             {
1855                 return *((byte*)dataPointer);
1856             }
1857             else if (dataType == typeof(sbyte))
1858             {
1859                 return *((sbyte*)dataPointer);
1860             }
1861             else if (dataType == typeof(short))
1862             {
1863                 return *((short*)dataPointer);
1864             }
1865             else if (dataType == typeof(ushort))
1866             {
1867                 return *((ushort*)dataPointer);
1868             }
1869             else if (dataType == typeof(float))
1870             {
1871                 return *((float*)dataPointer);
1872             }
1873             else if (dataType == typeof(double))
1874             {
1875                 return *((double*)dataPointer);
1876             }
1877             else if (dataType == typeof(decimal))
1878             {
1879                 return *((decimal*)dataPointer);
1880             }
1881             else if (dataType == typeof(bool))
1882             {
1883                 // The manifest defines a bool as a 32bit type (WIN32 BOOL), not 1 bit as CLR Does.
1884                 if (*((int*)dataPointer) == 1)
1885                 {
1886                     return true;
1887                 }
1888                 else
1889                 {
1890                     return false;
1891                 }
1892             }
1893             else if (dataType == typeof(Guid))
1894             {
1895                 return *((Guid*)dataPointer);
1896             }
1897             else if (dataType == typeof(char))
1898             {
1899                 return *((char*)dataPointer);
1900             }
1901             else if (dataType == typeof(DateTime))
1902             {
1903                 long dateTimeTicks = *((long*)dataPointer);
1904                 return DateTime.FromFileTimeUtc(dateTimeTicks);
1905             }
1906             else if (dataType == typeof(byte[]))
1907             {
1908                 // byte[] are written to EventData* as an int followed by a blob
1909                 int cbSize = *((int*)dataPointer);
1910                 byte[] blob = new byte[cbSize];
1911                 dataPointer = data->DataPointer;
1912                 data++;
1913                 for (int i = 0; i < cbSize; ++i)
1914                     blob[i] = *((byte*)(dataPointer + i));
1915                 return blob;
1916             }
1917             else if (dataType == typeof(byte*))
1918             {
1919                 // TODO: how do we want to handle this? For now we ignore it...
1920                 return null;
1921             }
1922             else
1923             {
1924                 if (m_EventSourcePreventRecursion && m_EventSourceInDecodeObject)
1925                 {
1926                     return null;
1927                 }
1928 
1929                 try
1930                 {
1931                     m_EventSourceInDecodeObject = true;
1932 
1933                     if (dataType.IsEnum())
1934                     {
1935                         dataType = Enum.GetUnderlyingType(dataType);
1936                         goto Again;
1937                     }
1938 
1939 
1940                     // Everything else is marshaled as a string.
1941                     // ETW strings are NULL-terminated, so marshal everything up to the first
1942                     // null in the string.
1943                     //return System.Runtime.InteropServices.Marshal.PtrToStringUni(dataPointer);
1944                     if(dataPointer == IntPtr.Zero)
1945                     {
1946                         return null;
1947                     }
1948 
1949                     return new string((char *)dataPointer);
1950 
1951                 }
1952                 finally
1953                 {
1954                     m_EventSourceInDecodeObject = false;
1955                 }
1956             }
1957         }
1958 
1959         // Finds the Dispatcher (which holds the filtering state), for a given dispatcher for the current
1960         // eventSource).
GetDispatcher(EventListener listener)1961         private EventDispatcher GetDispatcher(EventListener listener)
1962         {
1963             EventDispatcher dispatcher = m_Dispatchers;
1964             while (dispatcher != null)
1965             {
1966                 if (dispatcher.m_Listener == listener)
1967                     return dispatcher;
1968                 dispatcher = dispatcher.m_Next;
1969             }
1970             return dispatcher;
1971         }
1972 
WriteEventVarargs(int eventId, Guid* childActivityID, object[] args)1973         private unsafe void WriteEventVarargs(int eventId, Guid* childActivityID, object[] args)
1974         {
1975             if (m_eventSourceEnabled)
1976             {
1977                 try
1978                 {
1979                     Debug.Assert(m_eventData != null);  // You must have initialized this if you enabled the source.
1980                     if (childActivityID != null)
1981                     {
1982                         ValidateEventOpcodeForTransfer(ref m_eventData[eventId], m_eventData[eventId].Name);
1983 
1984                         // If you use WriteEventWithRelatedActivityID you MUST declare the first argument to be a GUID
1985                         // with the name 'relatedActivityID, and NOT pass this argument to the WriteEvent method.
1986                         // During manifest creation we modify the ParameterInfo[] that we store to strip out any
1987                         // first parameter that is of type Guid and named "relatedActivityId." Thus, if you call
1988                         // WriteEventWithRelatedActivityID from a method that doesn't name its first parameter correctly
1989                         // we can end up in a state where the ParameterInfo[] doesn't have its first parameter stripped,
1990                         // and this leads to a mismatch between the number of arguments and the number of ParameterInfos,
1991                         // which would cause a cryptic IndexOutOfRangeException later if we don't catch it here.
1992                         if (!m_eventData[eventId].HasRelatedActivityID)
1993                         {
1994                             throw new ArgumentException(SR.EventSource_NoRelatedActivityId);
1995                         }
1996                     }
1997 
1998                     LogEventArgsMismatches(m_eventData[eventId].Parameters, args);
1999 
2000                     Guid* pActivityId = null;
2001                     Guid activityId = Guid.Empty;
2002                     Guid relatedActivityId = Guid.Empty;
2003                     EventOpcode opcode = (EventOpcode)m_eventData[eventId].Descriptor.Opcode;
2004                     EventActivityOptions activityOptions = m_eventData[eventId].ActivityOptions;
2005 
2006                     if (childActivityID == null &&
2007                        ((activityOptions & EventActivityOptions.Disable) == 0))
2008                     {
2009                         if (opcode == EventOpcode.Start)
2010                         {
2011                             m_activityTracker.OnStart(m_name, m_eventData[eventId].Name, m_eventData[eventId].Descriptor.Task, ref activityId, ref relatedActivityId, m_eventData[eventId].ActivityOptions);
2012                         }
2013                         else if (opcode == EventOpcode.Stop)
2014                         {
2015                             m_activityTracker.OnStop(m_name, m_eventData[eventId].Name, m_eventData[eventId].Descriptor.Task, ref activityId);
2016                         }
2017 
2018                         if (activityId != Guid.Empty)
2019                             pActivityId = &activityId;
2020                         if (relatedActivityId != Guid.Empty)
2021                             childActivityID = &relatedActivityId;
2022                     }
2023 
2024 #if FEATURE_MANAGED_ETW
2025                     if (m_eventData[eventId].EnabledForETW)
2026                     {
2027 #if FEATURE_ACTIVITYSAMPLING
2028                         // this code should be kept in sync with WriteEventWithRelatedActivityIdCore().
2029                         SessionMask etwSessions = SessionMask.All;
2030                         // only compute etwSessions if there are *any* ETW filters enabled...
2031                         if ((ulong)m_curLiveSessions != 0)
2032                             etwSessions = GetEtwSessionMask(eventId, childActivityID);
2033 
2034                         if ((ulong)etwSessions != 0 || m_legacySessions != null && m_legacySessions.Count > 0)
2035                         {
2036                             if (!SelfDescribingEvents)
2037                             {
2038                                 if (etwSessions.IsEqualOrSupersetOf(m_curLiveSessions))
2039                                 {
2040                                     // by default the Descriptor.Keyword will have the perEventSourceSessionId bit
2041                                     // mask set to 0x0f so, when all ETW sessions want the event we don't need to
2042                                     // synthesize a new one
2043                                     if (!m_provider.WriteEvent(ref m_eventData[eventId].Descriptor, m_eventData[eventId].EventHandle, pActivityId, childActivityID, args))
2044                                         ThrowEventSourceException(m_eventData[eventId].Name);
2045                                 }
2046                                 else
2047                                 {
2048                                     long origKwd = unchecked((long)((ulong)m_eventData[eventId].Descriptor.Keywords & ~(SessionMask.All.ToEventKeywords())));
2049                                     // only some of the ETW sessions will receive this event. Synthesize a new
2050                                     // Descriptor whose Keywords field will have the appropriate bits set.
2051                                     var desc = new EventDescriptor(
2052                                         m_eventData[eventId].Descriptor.EventId,
2053                                         m_eventData[eventId].Descriptor.Version,
2054                                         m_eventData[eventId].Descriptor.Channel,
2055                                         m_eventData[eventId].Descriptor.Level,
2056                                         m_eventData[eventId].Descriptor.Opcode,
2057                                         m_eventData[eventId].Descriptor.Task,
2058                                         unchecked((long)etwSessions.ToEventKeywords() | origKwd));
2059 
2060                                     if (!m_provider.WriteEvent(ref desc, m_eventData[eventId].EventHandle, pActivityId, childActivityID, args))
2061                                         ThrowEventSourceException(m_eventData[eventId].Name);
2062                                 }
2063                             }
2064                             else
2065                             {
2066                                 TraceLoggingEventTypes tlet = m_eventData[eventId].TraceLoggingEventTypes;
2067                                 if (tlet == null)
2068                                 {
2069                                     tlet = new TraceLoggingEventTypes(m_eventData[eventId].Name,
2070                                                                         EventTags.None,
2071                                                                         m_eventData[eventId].Parameters);
2072                                     Interlocked.CompareExchange(ref m_eventData[eventId].TraceLoggingEventTypes, tlet, null);
2073 
2074                                 }
2075                                 long origKwd = unchecked((long)((ulong)m_eventData[eventId].Descriptor.Keywords & ~(SessionMask.All.ToEventKeywords())));
2076                                 // TODO: activity ID support
2077                                 EventSourceOptions opt = new EventSourceOptions
2078                                 {
2079                                     Keywords = (EventKeywords)unchecked((long)etwSessions.ToEventKeywords() | origKwd),
2080                                     Level = (EventLevel)m_eventData[eventId].Descriptor.Level,
2081                                     Opcode = (EventOpcode)m_eventData[eventId].Descriptor.Opcode
2082                                 };
2083 
2084                                 WriteMultiMerge(m_eventData[eventId].Name, ref opt, tlet, pActivityId, childActivityID, args);
2085                             }
2086                         }
2087 #else
2088                         if (!SelfDescribingEvents)
2089                         {
2090                             if (!m_provider.WriteEvent(ref m_eventData[eventId].Descriptor, m_eventData[eventId].EventHandle, pActivityId, childActivityID, args))
2091                                 ThrowEventSourceException(m_eventData[eventId].Name);
2092                         }
2093                         else
2094                         {
2095                             TraceLoggingEventTypes tlet = m_eventData[eventId].TraceLoggingEventTypes;
2096                             if (tlet == null)
2097                             {
2098                                 tlet = new TraceLoggingEventTypes(m_eventData[eventId].Name,
2099                                                                     EventTags.None,
2100                                                                     m_eventData[eventId].Parameters);
2101                                 Interlocked.CompareExchange(ref m_eventData[eventId].TraceLoggingEventTypes, tlet, null);
2102 
2103                             }
2104                             // TODO: activity ID support
2105                             EventSourceOptions opt = new EventSourceOptions
2106                             {
2107                                 Keywords = (EventKeywords)m_eventData[eventId].Descriptor.Keywords,
2108                                 Level = (EventLevel)m_eventData[eventId].Descriptor.Level,
2109                                 Opcode = (EventOpcode)m_eventData[eventId].Descriptor.Opcode
2110                             };
2111 
2112                             WriteMultiMerge(m_eventData[eventId].Name, ref opt, tlet, pActivityId, childActivityID, args);
2113                         }
2114 #endif // FEATURE_ACTIVITYSAMPLING
2115                     }
2116 #endif // FEATURE_MANAGED_ETW
2117                     if (m_Dispatchers != null && m_eventData[eventId].EnabledForAnyListener)
2118                     {
2119 #if (!ES_BUILD_STANDALONE && !ES_BUILD_PN)
2120                         // Maintain old behavior - object identity is preserved
2121                         if (AppContextSwitches.PreserveEventListnerObjectIdentity)
2122                         {
2123                             WriteToAllListeners(eventId, childActivityID, args);
2124                         }
2125                         else
2126 #endif // !ES_BUILD_STANDALONE
2127                         {
2128                             object[] serializedArgs = SerializeEventArgs(eventId, args);
2129                             WriteToAllListeners(eventId, childActivityID, serializedArgs);
2130                         }
2131                     }
2132                 }
2133                 catch (Exception ex)
2134                 {
2135                     if (ex is EventSourceException)
2136                         throw;
2137                     else
2138                         ThrowEventSourceException(m_eventData[eventId].Name, ex);
2139                 }
2140             }
2141         }
2142 
SerializeEventArgs(int eventId, object[] args)2143         unsafe private object[] SerializeEventArgs(int eventId, object[] args)
2144         {
2145             TraceLoggingEventTypes eventTypes = m_eventData[eventId].TraceLoggingEventTypes;
2146             if (eventTypes == null)
2147             {
2148                 eventTypes = new TraceLoggingEventTypes(m_eventData[eventId].Name,
2149                                                         EventTags.None,
2150                                                         m_eventData[eventId].Parameters);
2151                 Interlocked.CompareExchange(ref m_eventData[eventId].TraceLoggingEventTypes, eventTypes, null);
2152             }
2153             var eventData = new object[eventTypes.typeInfos.Length];
2154             for (int i = 0; i < eventTypes.typeInfos.Length; i++)
2155             {
2156                 eventData[i] = eventTypes.typeInfos[i].GetData(args[i]);
2157             }
2158             return eventData;
2159         }
2160 
2161         /// <summary>
2162         /// We expect that the arguments to the Event method and the arguments to WriteEvent match. This function
2163         /// checks that they in fact match and logs a warning to the debugger if they don't.
2164         /// </summary>
2165         /// <param name="infos"></param>
2166         /// <param name="args"></param>
LogEventArgsMismatches(ParameterInfo[] infos, object[] args)2167         private void LogEventArgsMismatches(ParameterInfo[] infos, object[] args)
2168         {
2169 #if (!ES_BUILD_PCL && !ES_BUILD_PN)
2170             // It would be nice to have this on PCL builds, but it would be pointless since there isn't support for
2171             // writing to the debugger log on PCL.
2172             bool typesMatch = args.Length == infos.Length;
2173 
2174             int i = 0;
2175             while (typesMatch && i < args.Length)
2176             {
2177                 Type pType = infos[i].ParameterType;
2178 
2179                 // Checking to see if the Parameter types (from the Event method) match the supplied argument types.
2180                 // Fail if one of two things hold : either the argument type is not equal to the parameter type, or the
2181                 // argument is null and the parameter type is non-nullable.
2182                 if ((args[i] != null && (args[i].GetType() != pType))
2183                     || (args[i] == null && (!(pType.IsGenericType && pType.GetGenericTypeDefinition() == typeof(Nullable<>))))
2184                     )
2185                 {
2186                     typesMatch = false;
2187                     break;
2188                 }
2189 
2190                 ++i;
2191             }
2192 
2193             if (!typesMatch)
2194             {
2195                 System.Diagnostics.Debugger.Log(0, null, SR.EventSource_VarArgsParameterMismatch + "\r\n");
2196             }
2197 #endif //!ES_BUILD_PCL
2198         }
2199 
WriteToAllListeners(int eventId, Guid* childActivityID, int eventDataCount, EventSource.EventData* data)2200         unsafe private void WriteToAllListeners(int eventId, Guid* childActivityID, int eventDataCount, EventSource.EventData* data)
2201         {
2202             // We represent a byte[] as a integer denoting the length  and then a blob of bytes in the data pointer. This causes a spurious
2203             // warning because eventDataCount is off by one for the byte[] case since a byte[] has 2 items associated it. So we want to check
2204             // that the number of parameters is correct against the byte[] case, but also we the args array would be one too long if
2205             // we just used the modifiedParamCount here -- so we need both.
2206             int paramCount = GetParameterCount(m_eventData[eventId]);
2207             int modifiedParamCount = 0;
2208             for (int i = 0; i < paramCount; i++)
2209             {
2210                 Type parameterType = GetDataType(m_eventData[eventId], i);
2211                 if (parameterType == typeof(byte[]))
2212                 {
2213                     modifiedParamCount += 2;
2214                 }
2215                 else
2216                 {
2217                     modifiedParamCount++;
2218                 }
2219             }
2220             if (eventDataCount != modifiedParamCount)
2221             {
2222                 ReportOutOfBandMessage(SR.Format(SR.EventSource_EventParametersMismatch, eventId, eventDataCount, paramCount), true);
2223                 paramCount = Math.Min(paramCount, eventDataCount);
2224             }
2225 
2226             object[] args = new object[paramCount];
2227 
2228             EventSource.EventData* dataPtr = data;
2229             for (int i = 0; i < paramCount; i++)
2230                 args[i] = DecodeObject(eventId, i, ref dataPtr);
2231             WriteToAllListeners(eventId, childActivityID, args);
2232         }
2233 
2234         // helper for writing to all EventListeners attached the current eventSource.
WriteToAllListeners(int eventId, Guid* childActivityID, params object[] args)2235         unsafe private void WriteToAllListeners(int eventId, Guid* childActivityID, params object[] args)
2236         {
2237             EventWrittenEventArgs eventCallbackArgs = new EventWrittenEventArgs(this);
2238             eventCallbackArgs.EventId = eventId;
2239             if (childActivityID != null)
2240                 eventCallbackArgs.RelatedActivityId = *childActivityID;
2241             eventCallbackArgs.EventName = m_eventData[eventId].Name;
2242             eventCallbackArgs.Message = m_eventData[eventId].Message;
2243             eventCallbackArgs.Payload = new ReadOnlyCollection<object>(args);
2244 
2245             DispatchToAllListeners(eventId, childActivityID, eventCallbackArgs);
2246         }
2247 
DispatchToAllListeners(int eventId, Guid* childActivityID, EventWrittenEventArgs eventCallbackArgs)2248         private unsafe void DispatchToAllListeners(int eventId, Guid* childActivityID, EventWrittenEventArgs eventCallbackArgs)
2249         {
2250             Exception lastThrownException = null;
2251             for (EventDispatcher dispatcher = m_Dispatchers; dispatcher != null; dispatcher = dispatcher.m_Next)
2252             {
2253                 Debug.Assert(dispatcher.m_EventEnabled != null);
2254                 if (eventId == -1 || dispatcher.m_EventEnabled[eventId])
2255                 {
2256 #if FEATURE_ACTIVITYSAMPLING
2257                     var activityFilter = dispatcher.m_Listener.m_activityFilter;
2258                     // order below is important as PassesActivityFilter will "flow" active activities
2259                     // even when the current EventSource doesn't have filtering enabled. This allows
2260                     // interesting activities to be updated so that sources that do sample can get
2261                     // accurate data
2262                     if (activityFilter == null ||
2263                         ActivityFilter.PassesActivityFilter(activityFilter, childActivityID,
2264                                                             m_eventData[eventId].TriggersActivityTracking > 0,
2265                                                             this, eventId) ||
2266                         !dispatcher.m_activityFilteringEnabled)
2267 #endif // FEATURE_ACTIVITYSAMPLING
2268                     {
2269                         try
2270                         {
2271                             dispatcher.m_Listener.OnEventWritten(eventCallbackArgs);
2272                         }
2273                         catch (Exception e)
2274                         {
2275                             ReportOutOfBandMessage("ERROR: Exception during EventSource.OnEventWritten: "
2276                                  + e.Message, false);
2277                             lastThrownException = e;
2278                         }
2279                     }
2280                 }
2281             }
2282 
2283             if (lastThrownException != null)
2284             {
2285                 throw new EventSourceException(lastThrownException);
2286             }
2287         }
2288 
2289         [SuppressMessage("Microsoft.Concurrency", "CA8001", Justification = "This does not need to be correct when racing with other threads")]
WriteEventString(EventLevel level, long keywords, string msgString)2290         private unsafe void WriteEventString(EventLevel level, long keywords, string msgString)
2291         {
2292 #if FEATURE_MANAGED_ETW && !FEATURE_PERFTRACING
2293             if (m_provider != null)
2294             {
2295                 string eventName = "EventSourceMessage";
2296                 if (SelfDescribingEvents)
2297                 {
2298                     EventSourceOptions opt = new EventSourceOptions
2299                     {
2300                         Keywords = (EventKeywords)unchecked(keywords),
2301                         Level = level
2302                     };
2303                     var msg = new { message = msgString };
2304                     var tlet = new TraceLoggingEventTypes(eventName, EventTags.None, new Type[] { msg.GetType() });
2305                     WriteMultiMergeInner(eventName, ref opt, tlet, null, null, msg);
2306                 }
2307                 else
2308                 {
2309                     // We want the name of the provider to show up so if we don't have a manifest we create
2310                     // on that at least has the provider name (I don't define any events).
2311                     if (m_rawManifest == null && m_outOfBandMessageCount == 1)
2312                     {
2313                         ManifestBuilder manifestBuilder = new ManifestBuilder(Name, Guid, Name, null, EventManifestOptions.None);
2314                         manifestBuilder.StartEvent(eventName, new EventAttribute(0) { Level = EventLevel.LogAlways, Task = (EventTask)0xFFFE });
2315                         manifestBuilder.AddEventParameter(typeof(string), "message");
2316                         manifestBuilder.EndEvent();
2317                         SendManifest(manifestBuilder.CreateManifest());
2318                     }
2319 
2320                     // We use this low level routine to to bypass the enabled checking, since the eventSource itself is only partially inited.
2321                     fixed (char* msgStringPtr = msgString)
2322                     {
2323                         EventDescriptor descr = new EventDescriptor(0, 0, 0, (byte)level, 0, 0, keywords);
2324                         EventProvider.EventData data = new EventProvider.EventData();
2325                         data.Ptr = (ulong)msgStringPtr;
2326                         data.Size = (uint)(2 * (msgString.Length + 1));
2327                         data.Reserved = 0;
2328                         m_provider.WriteEvent(ref descr, IntPtr.Zero, null, null, 1, (IntPtr)((void*)&data));
2329                     }
2330                 }
2331             }
2332 #endif // FEATURE_MANAGED_ETW
2333         }
2334 
2335         /// <summary>
2336         /// Since this is a means of reporting errors (see ReportoutOfBandMessage) any failure encountered
2337         /// while writing the message to any one of the listeners will be silently ignored.
2338         /// </summary>
WriteStringToAllListeners(string eventName, string msg)2339         private void WriteStringToAllListeners(string eventName, string msg)
2340         {
2341             EventWrittenEventArgs eventCallbackArgs = new EventWrittenEventArgs(this);
2342             eventCallbackArgs.EventId = 0;
2343             eventCallbackArgs.Message = msg;
2344             eventCallbackArgs.Payload = new ReadOnlyCollection<object>(new List<object>() { msg });
2345             eventCallbackArgs.PayloadNames = new ReadOnlyCollection<string>(new List<string> { "message" });
2346             eventCallbackArgs.EventName = eventName;
2347 
2348             for (EventDispatcher dispatcher = m_Dispatchers; dispatcher != null; dispatcher = dispatcher.m_Next)
2349             {
2350                 bool dispatcherEnabled = false;
2351                 if (dispatcher.m_EventEnabled == null)
2352                 {
2353                     // if the listeners that weren't correctly initialized, we will send to it
2354                     // since this is an error message and we want to see it go out.
2355                     dispatcherEnabled = true;
2356                 }
2357                 else
2358                 {
2359                     // if there's *any* enabled event on the dispatcher we'll write out the string
2360                     // otherwise we'll treat the listener as disabled and skip it
2361                     for (int evtId = 0; evtId < dispatcher.m_EventEnabled.Length; ++evtId)
2362                     {
2363                         if (dispatcher.m_EventEnabled[evtId])
2364                         {
2365                             dispatcherEnabled = true;
2366                             break;
2367                         }
2368                     }
2369                 }
2370                 try
2371                 {
2372                     if (dispatcherEnabled)
2373                         dispatcher.m_Listener.OnEventWritten(eventCallbackArgs);
2374                 }
2375                 catch
2376                 {
2377                     // ignore any exceptions thrown by listeners' OnEventWritten
2378                 }
2379             }
2380         }
2381 
2382 #if FEATURE_ACTIVITYSAMPLING
GetEtwSessionMask(int eventId, Guid* childActivityID)2383         unsafe private SessionMask GetEtwSessionMask(int eventId, Guid* childActivityID)
2384         {
2385             SessionMask etwSessions = new SessionMask();
2386 
2387             for (int i = 0; i < SessionMask.MAX; ++i)
2388             {
2389                 EtwSession etwSession = m_etwSessionIdMap[i];
2390                 if (etwSession != null)
2391                 {
2392                     ActivityFilter activityFilter = etwSession.m_activityFilter;
2393                     // PassesActivityFilter() will flow "interesting" activities, so make sure
2394                     // to perform this test first, before ORing with ~m_activityFilteringForETWEnabled
2395                     // (note: the first test for !m_activityFilteringForETWEnabled[i] ensures we
2396                     //  do not fire events indiscriminately, when no filters are specified, but only
2397                     //  if, in addition, the session did not also enable ActivitySampling)
2398                     if (activityFilter == null && !m_activityFilteringForETWEnabled[i] ||
2399                         activityFilter != null &&
2400                             ActivityFilter.PassesActivityFilter(activityFilter, childActivityID,
2401                                 m_eventData[eventId].TriggersActivityTracking > 0, this, eventId) ||
2402                         !m_activityFilteringForETWEnabled[i])
2403                     {
2404                         etwSessions[i] = true;
2405                     }
2406                 }
2407             }
2408             // flow "interesting" activities for all legacy sessions in which there's some
2409             // level of activity tracing enabled (even other EventSources)
2410             if (m_legacySessions != null && m_legacySessions.Count > 0 &&
2411                 (EventOpcode)m_eventData[eventId].Descriptor.Opcode == EventOpcode.Send)
2412             {
2413                 // only calculate InternalCurrentThreadActivityId once
2414                 Guid* pCurrentActivityId = null;
2415                 Guid currentActivityId;
2416                 foreach (var legacyEtwSession in m_legacySessions)
2417                 {
2418                     if (legacyEtwSession == null)
2419                         continue;
2420 
2421                     ActivityFilter activityFilter = legacyEtwSession.m_activityFilter;
2422                     if (activityFilter != null)
2423                     {
2424                         if (pCurrentActivityId == null)
2425                         {
2426                             currentActivityId = InternalCurrentThreadActivityId;
2427                             pCurrentActivityId = &currentActivityId;
2428                         }
2429                         ActivityFilter.FlowActivityIfNeeded(activityFilter, pCurrentActivityId, childActivityID);
2430                     }
2431                 }
2432             }
2433 
2434             return etwSessions;
2435         }
2436 #endif // FEATURE_ACTIVITYSAMPLING
2437 
2438         /// <summary>
2439         /// Returns true if 'eventNum' is enabled if you only consider the level and matchAnyKeyword filters.
2440         /// It is possible that eventSources turn off the event based on additional filtering criteria.
2441         /// </summary>
IsEnabledByDefault(int eventNum, bool enable, EventLevel currentLevel, EventKeywords currentMatchAnyKeyword)2442         private bool IsEnabledByDefault(int eventNum, bool enable, EventLevel currentLevel, EventKeywords currentMatchAnyKeyword)
2443         {
2444             if (!enable)
2445                 return false;
2446 
2447             EventLevel eventLevel = (EventLevel)m_eventData[eventNum].Descriptor.Level;
2448             EventKeywords eventKeywords = unchecked((EventKeywords)((ulong)m_eventData[eventNum].Descriptor.Keywords & (~(SessionMask.All.ToEventKeywords()))));
2449 
2450 #if FEATURE_MANAGED_ETW_CHANNELS
2451             EventChannel channel = unchecked((EventChannel)m_eventData[eventNum].Descriptor.Channel);
2452 #else
2453             EventChannel channel = EventChannel.None;
2454 #endif
2455 
2456             return IsEnabledCommon(enable, currentLevel, currentMatchAnyKeyword, eventLevel, eventKeywords, channel);
2457         }
2458 
IsEnabledCommon(bool enabled, EventLevel currentLevel, EventKeywords currentMatchAnyKeyword, EventLevel eventLevel, EventKeywords eventKeywords, EventChannel eventChannel)2459         private bool IsEnabledCommon(bool enabled, EventLevel currentLevel, EventKeywords currentMatchAnyKeyword,
2460                                                           EventLevel eventLevel, EventKeywords eventKeywords, EventChannel eventChannel)
2461         {
2462             if (!enabled)
2463                 return false;
2464 
2465             // does is pass the level test?
2466             if ((currentLevel != 0) && (currentLevel < eventLevel))
2467                 return false;
2468 
2469             // if yes, does it pass the keywords test?
2470             if (currentMatchAnyKeyword != 0 && eventKeywords != 0)
2471             {
2472 #if FEATURE_MANAGED_ETW_CHANNELS
2473                 // is there a channel with keywords that match currentMatchAnyKeyword?
2474                 if (eventChannel != EventChannel.None && this.m_channelData != null && this.m_channelData.Length > (int)eventChannel)
2475                 {
2476                     EventKeywords channel_keywords = unchecked((EventKeywords)(m_channelData[(int)eventChannel] | (ulong)eventKeywords));
2477                     if (channel_keywords != 0 && (channel_keywords & currentMatchAnyKeyword) == 0)
2478                         return false;
2479                 }
2480                 else
2481 #endif
2482                 {
2483                     if ((unchecked((ulong)eventKeywords & (ulong)currentMatchAnyKeyword)) == 0)
2484                         return false;
2485                 }
2486             }
2487             return true;
2488 
2489         }
2490         [System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.NoInlining)]
ThrowEventSourceException(string eventName, Exception innerEx = null)2491         private void ThrowEventSourceException(string eventName, Exception innerEx = null)
2492         {
2493             // If we fail during out of band logging we may end up trying
2494             // to throw another EventSourceException, thus hitting a StackOverflowException.
2495             // Avoid StackOverflow by making sure we do not recursively call this method.
2496             if (m_EventSourceExceptionRecurenceCount > 0)
2497                 return;
2498             try
2499             {
2500                 m_EventSourceExceptionRecurenceCount++;
2501 
2502                 string errorPrefix = "EventSourceException";
2503                 if (eventName != null)
2504                 {
2505                     errorPrefix += " while processing event \"" + eventName + "\"";
2506                 }
2507 
2508                 // TODO Create variations of EventSourceException that indicate more information using the error code.
2509                 switch (EventProvider.GetLastWriteEventError())
2510                 {
2511                     case EventProvider.WriteEventErrorCode.EventTooBig:
2512                         ReportOutOfBandMessage(errorPrefix + ": " + SR.EventSource_EventTooBig, true);
2513                         if (ThrowOnEventWriteErrors) throw new EventSourceException(SR.EventSource_EventTooBig, innerEx);
2514                         break;
2515                     case EventProvider.WriteEventErrorCode.NoFreeBuffers:
2516                         ReportOutOfBandMessage(errorPrefix + ": " + SR.EventSource_NoFreeBuffers, true);
2517                         if (ThrowOnEventWriteErrors) throw new EventSourceException(SR.EventSource_NoFreeBuffers, innerEx);
2518                         break;
2519                     case EventProvider.WriteEventErrorCode.NullInput:
2520                         ReportOutOfBandMessage(errorPrefix + ": " + SR.EventSource_NullInput, true);
2521                         if (ThrowOnEventWriteErrors) throw new EventSourceException(SR.EventSource_NullInput, innerEx);
2522                         break;
2523                     case EventProvider.WriteEventErrorCode.TooManyArgs:
2524                         ReportOutOfBandMessage(errorPrefix + ": " + SR.EventSource_TooManyArgs, true);
2525                         if (ThrowOnEventWriteErrors) throw new EventSourceException(SR.EventSource_TooManyArgs, innerEx);
2526                         break;
2527                     default:
2528                         if (innerEx != null)
2529                             ReportOutOfBandMessage(errorPrefix + ": " + innerEx.GetType() + ":" + innerEx.Message, true);
2530                         else
2531                             ReportOutOfBandMessage(errorPrefix, true);
2532                         if (ThrowOnEventWriteErrors) throw new EventSourceException(innerEx);
2533                         break;
2534                 }
2535             }
2536             finally
2537             {
2538                 m_EventSourceExceptionRecurenceCount--;
2539             }
2540         }
2541 
ValidateEventOpcodeForTransfer(ref EventMetadata eventData, string eventName)2542         private void ValidateEventOpcodeForTransfer(ref EventMetadata eventData, string eventName)
2543         {
2544             if ((EventOpcode)eventData.Descriptor.Opcode != EventOpcode.Send &&
2545                 (EventOpcode)eventData.Descriptor.Opcode != EventOpcode.Receive &&
2546                 (EventOpcode)eventData.Descriptor.Opcode != EventOpcode.Start)
2547             {
2548                 ThrowEventSourceException(eventName);
2549             }
2550         }
2551 
GetOpcodeWithDefault(EventOpcode opcode, string eventName)2552         internal static EventOpcode GetOpcodeWithDefault(EventOpcode opcode, string eventName)
2553         {
2554             if (opcode == EventOpcode.Info && eventName != null)
2555             {
2556                 if (eventName.EndsWith(s_ActivityStartSuffix, StringComparison.Ordinal))
2557                 {
2558                     return EventOpcode.Start;
2559                 }
2560                 else if (eventName.EndsWith(s_ActivityStopSuffix, StringComparison.Ordinal))
2561                 {
2562                     return EventOpcode.Stop;
2563                 }
2564             }
2565 
2566             return opcode;
2567         }
2568 
2569 #if FEATURE_MANAGED_ETW
2570         /// <summary>
2571         /// This class lets us hook the 'OnEventCommand' from the eventSource.
2572         /// </summary>
2573         private class OverideEventProvider : EventProvider
2574         {
OverideEventProvider(EventSource eventSource)2575             public OverideEventProvider(EventSource eventSource)
2576             {
2577                 this.m_eventSource = eventSource;
2578             }
OnControllerCommand(ControllerCommand command, IDictionary<string, string> arguments, int perEventSourceSessionId, int etwSessionId)2579             protected override void OnControllerCommand(ControllerCommand command, IDictionary<string, string> arguments,
2580                                                               int perEventSourceSessionId, int etwSessionId)
2581             {
2582                 // We use null to represent the ETW EventListener.
2583                 EventListener listener = null;
2584                 m_eventSource.SendCommand(listener, perEventSourceSessionId, etwSessionId,
2585                                           (EventCommand)command, IsEnabled(), Level, MatchAnyKeyword, arguments);
2586             }
2587             private EventSource m_eventSource;
2588         }
2589 #endif
2590 
2591         /// <summary>
2592         /// Used to hold all the static information about an event.  This includes everything in the event
2593         /// descriptor as well as some stuff we added specifically for EventSource. see the
2594         /// code:m_eventData for where we use this.
2595         /// </summary>
2596 
2597         /*
2598          EventMetadata was public in the separate System.Diagnostics.Tracing assembly(pre NS2.0),
2599          now the move to CoreLib marked them as private.
2600          While they are technically private (it's a contract used between the library and the ILC toolchain),
2601          we need them to be rooted and exported from shared library for the system to work.
2602          For now I'm simply marking them as public again.A cleaner solution might be to use.rd.xml to
2603          root them and modify shared library definition to force export them.
2604          */
2605 #if ES_BUILD_PN
2606        public
2607 #else
2608        internal
2609 #endif
2610         partial struct EventMetadata
2611         {
2612             public EventDescriptor Descriptor;
2613             public IntPtr EventHandle;              // EventPipeEvent handle.
2614             public EventTags Tags;
2615             public bool EnabledForAnyListener;      // true if any dispatcher has this event turned on
2616             public bool EnabledForETW;              // is this event on for the OS ETW data dispatcher?
2617 
2618             public bool HasRelatedActivityID;       // Set if the event method's first parameter is a Guid named 'relatedActivityId'
2619 #if !FEATURE_ACTIVITYSAMPLING
2620 #pragma warning disable 0649
2621 #endif
2622             public byte TriggersActivityTracking;   // count of listeners that marked this event as trigger for start of activity logging.
2623 #if !FEATURE_ACTIVITYSAMPLING
2624 #pragma warning restore 0649
2625 #endif
2626             public string Name;                     // the name of the event
2627             public string Message;                  // If the event has a message associated with it, this is it.
2628             public ParameterInfo[] Parameters;      // TODO can we remove?
2629 
2630             public TraceLoggingEventTypes TraceLoggingEventTypes;
2631             public EventActivityOptions ActivityOptions;
2632 
2633 #if ES_BUILD_PN
2634             public EventParameterType[] ParameterTypes;
2635 #endif
2636         };
2637 
2638         // This is the internal entry point that code:EventListeners call when wanting to send a command to a
2639         // eventSource. The logic is as follows
2640         //
2641         // * if Command == Update
2642         //     * perEventSourceSessionId specifies the per-provider ETW session ID that the command applies
2643         //         to (if listener != null)
2644         //         perEventSourceSessionId = 0 - reserved for EventListeners
2645         //         perEventSourceSessionId = 1..SessionMask.MAX - reserved for activity tracing aware ETW sessions
2646         //                  perEventSourceSessionId-1 represents the bit in the reserved field (bits 44..47) in
2647         //                  Keywords that identifies the session
2648         //         perEventSourceSessionId = SessionMask.MAX+1 - reserved for legacy ETW sessions; these are
2649         //                  discriminated by etwSessionId
2650         //     * etwSessionId specifies a machine-wide ETW session ID; this allows correlation of
2651         //         activity tracing across different providers (which might have different sessionIds
2652         //         for the same ETW session)
2653         //     * enable, level, matchAnyKeywords are used to set a default for all events for the
2654         //         eventSource.  In particular, if 'enabled' is false, 'level' and
2655         //         'matchAnyKeywords' are not used.
2656         //     * OnEventCommand is invoked, which may cause calls to
2657         //         code:EventSource.EnableEventForDispatcher which may cause changes in the filtering
2658         //         depending on the logic in that routine.
2659         // * else (command != Update)
2660         //     * Simply call OnEventCommand. The expectation is that filtering is NOT changed.
2661         //     * The 'enabled' 'level', matchAnyKeyword' arguments are ignored (must be true, 0, 0).
2662         //
2663         // dispatcher == null has special meaning. It is the 'ETW' dispatcher.
SendCommand(EventListener listener, int perEventSourceSessionId, int etwSessionId, EventCommand command, bool enable, EventLevel level, EventKeywords matchAnyKeyword, IDictionary<string, string> commandArguments)2664         internal void SendCommand(EventListener listener, int perEventSourceSessionId, int etwSessionId,
2665                                   EventCommand command, bool enable,
2666                                   EventLevel level, EventKeywords matchAnyKeyword,
2667                                   IDictionary<string, string> commandArguments)
2668         {
2669             var commandArgs = new EventCommandEventArgs(command, commandArguments, this, listener, perEventSourceSessionId, etwSessionId, enable, level, matchAnyKeyword);
2670             lock (EventListener.EventListenersLock)
2671             {
2672                 if (m_completelyInited)
2673                 {
2674                     // After the first command arrive after construction, we are ready to get rid of the deferred commands
2675                     this.m_deferredCommands = null;
2676                     // We are fully initialized, do the command
2677                     DoCommand(commandArgs);
2678                 }
2679                 else
2680                 {
2681                     // We can't do the command, simply remember it and we do it when we are fully constructed.
2682                     if (m_deferredCommands == null)
2683                         m_deferredCommands = commandArgs;       // create the first entry
2684                     else
2685                     {
2686                         // We have one or more entries, find the last one and add it to that.
2687                         EventCommandEventArgs lastCommand = m_deferredCommands;
2688                         while (lastCommand.nextCommand != null)
2689                             lastCommand = lastCommand.nextCommand;
2690                         lastCommand.nextCommand = commandArgs;
2691                     }
2692                 }
2693             }
2694         }
2695 
2696         /// <summary>
2697         /// We want the eventSource to be fully initialized when we do commands because that way we can send
2698         /// error messages and other logging directly to the event stream.   Unfortunately we can get callbacks
2699         /// when we are not fully initialized.  In that case we store them in 'commandArgs' and do them later.
2700         /// This helper actually does all actual command logic.
2701         /// </summary>
DoCommand(EventCommandEventArgs commandArgs)2702         internal void DoCommand(EventCommandEventArgs commandArgs)
2703         {
2704             // PRECONDITION: We should be holding the EventListener.EventListenersLock
2705             // We defer commands until we are completely inited.  This allows error messages to be sent.
2706             Debug.Assert(m_completelyInited);
2707 
2708 #if FEATURE_MANAGED_ETW
2709             if (m_provider == null)     // If we failed to construct
2710                 return;
2711 #endif // FEATURE_MANAGED_ETW
2712 
2713             m_outOfBandMessageCount = 0;
2714             bool shouldReport = (commandArgs.perEventSourceSessionId > 0) && (commandArgs.perEventSourceSessionId <= SessionMask.MAX);
2715             try
2716             {
2717                 EnsureDescriptorsInitialized();
2718                 Debug.Assert(m_eventData != null);
2719 
2720                 // Find the per-EventSource dispatcher corresponding to registered dispatcher
2721                 commandArgs.dispatcher = GetDispatcher(commandArgs.listener);
2722                 if (commandArgs.dispatcher == null && commandArgs.listener != null)     // dispatcher == null means ETW dispatcher
2723                 {
2724                     throw new ArgumentException(SR.EventSource_ListenerNotFound);
2725                 }
2726 
2727                 if (commandArgs.Arguments == null)
2728                     commandArgs.Arguments = new Dictionary<string, string>();
2729 
2730                 if (commandArgs.Command == EventCommand.Update)
2731                 {
2732                     // Set it up using the 'standard' filtering bitfields (use the "global" enable, not session specific one)
2733                     for (int i = 0; i < m_eventData.Length; i++)
2734                         EnableEventForDispatcher(commandArgs.dispatcher, i, IsEnabledByDefault(i, commandArgs.enable, commandArgs.level, commandArgs.matchAnyKeyword));
2735 
2736                     if (commandArgs.enable)
2737                     {
2738                         if (!m_eventSourceEnabled)
2739                         {
2740                             // EventSource turned on for the first time, simply copy the bits.
2741                             m_level = commandArgs.level;
2742                             m_matchAnyKeyword = commandArgs.matchAnyKeyword;
2743                         }
2744                         else
2745                         {
2746                             // Already enabled, make it the most verbose of the existing and new filter
2747                             if (commandArgs.level > m_level)
2748                                 m_level = commandArgs.level;
2749                             if (commandArgs.matchAnyKeyword == 0)
2750                                 m_matchAnyKeyword = 0;
2751                             else if (m_matchAnyKeyword != 0)
2752                                 m_matchAnyKeyword = unchecked(m_matchAnyKeyword | commandArgs.matchAnyKeyword);
2753                         }
2754                     }
2755 
2756                     // interpret perEventSourceSessionId's sign, and adjust perEventSourceSessionId to
2757                     // represent 0-based positive values
2758                     bool bSessionEnable = (commandArgs.perEventSourceSessionId >= 0);
2759                     if (commandArgs.perEventSourceSessionId == 0 && commandArgs.enable == false)
2760                         bSessionEnable = false;
2761 
2762                     if (commandArgs.listener == null)
2763                     {
2764                         if (!bSessionEnable)
2765                             commandArgs.perEventSourceSessionId = -commandArgs.perEventSourceSessionId;
2766                         // for "global" enable/disable (passed in with listener == null and
2767                         //  perEventSourceSessionId == 0) perEventSourceSessionId becomes -1
2768                         --commandArgs.perEventSourceSessionId;
2769                     }
2770 
2771                     commandArgs.Command = bSessionEnable ? EventCommand.Enable : EventCommand.Disable;
2772 
2773                     // perEventSourceSessionId = -1 when ETW sent a notification, but the set of active sessions
2774                     // hasn't changed.
2775                     // sesisonId = SessionMask.MAX when one of the legacy ETW sessions changed
2776                     // 0 <= perEventSourceSessionId < SessionMask.MAX for activity-tracing aware sessions
2777                     Debug.Assert(commandArgs.perEventSourceSessionId >= -1 && commandArgs.perEventSourceSessionId <= SessionMask.MAX);
2778 
2779                     // Send the manifest if we are enabling an ETW session
2780                     if (bSessionEnable && commandArgs.dispatcher == null)
2781                     {
2782                         // eventSourceDispatcher == null means this is the ETW manifest
2783 
2784 #if !FEATURE_PERFTRACING
2785                         // Note that we unconditionally send the manifest whenever we are enabled, even if
2786                         // we were already enabled.   This is because there may be multiple sessions active
2787                         // and we can't know that all the sessions have seen the manifest.
2788                         if (!SelfDescribingEvents)
2789                             SendManifest(m_rawManifest);
2790 #endif
2791                     }
2792 
2793 #if FEATURE_ACTIVITYSAMPLING
2794                     if (bSessionEnable && commandArgs.perEventSourceSessionId != -1)
2795                     {
2796                         bool participateInSampling = false;
2797                         string activityFilters;
2798                         int sessionIdBit;
2799 
2800                         ParseCommandArgs(commandArgs.Arguments, out participateInSampling,
2801                                             out activityFilters, out sessionIdBit);
2802 
2803                         if (commandArgs.listener == null && commandArgs.Arguments.Count > 0 && commandArgs.perEventSourceSessionId != sessionIdBit)
2804                         {
2805                             throw new ArgumentException(SR.Format(SR.EventSource_SessionIdError,
2806                                                     commandArgs.perEventSourceSessionId + SessionMask.SHIFT_SESSION_TO_KEYWORD,
2807                                                         sessionIdBit + SessionMask.SHIFT_SESSION_TO_KEYWORD));
2808                         }
2809 
2810                         if (commandArgs.listener == null)
2811                         {
2812                             UpdateEtwSession(commandArgs.perEventSourceSessionId, commandArgs.etwSessionId, true, activityFilters, participateInSampling);
2813                         }
2814                         else
2815                         {
2816                             ActivityFilter.UpdateFilter(ref commandArgs.listener.m_activityFilter, this, 0, activityFilters);
2817                             commandArgs.dispatcher.m_activityFilteringEnabled = participateInSampling;
2818                         }
2819                     }
2820                     else if (!bSessionEnable && commandArgs.listener == null)
2821                     {
2822                         // if we disable an ETW session, indicate that in a synthesized command argument
2823                         if (commandArgs.perEventSourceSessionId >= 0 && commandArgs.perEventSourceSessionId < SessionMask.MAX)
2824                         {
2825                             commandArgs.Arguments["EtwSessionKeyword"] = (commandArgs.perEventSourceSessionId + SessionMask.SHIFT_SESSION_TO_KEYWORD).ToString(CultureInfo.InvariantCulture);
2826                         }
2827                     }
2828 #endif // FEATURE_ACTIVITYSAMPLING
2829 
2830                     // Turn on the enable bit before making the OnEventCommand callback  This allows you to do useful
2831                     // things like log messages, or test if keywords are enabled in the callback.
2832                     if (commandArgs.enable)
2833                     {
2834                         Debug.Assert(m_eventData != null);
2835                         m_eventSourceEnabled = true;
2836                     }
2837 
2838                     this.OnEventCommand(commandArgs);
2839                     var eventCommandCallback = this.m_eventCommandExecuted;
2840                     if (eventCommandCallback != null)
2841                         eventCommandCallback(this, commandArgs);
2842 
2843 #if FEATURE_ACTIVITYSAMPLING
2844                     if (commandArgs.listener == null && !bSessionEnable && commandArgs.perEventSourceSessionId != -1)
2845                     {
2846                         // if we disable an ETW session, complete disabling it
2847                         UpdateEtwSession(commandArgs.perEventSourceSessionId, commandArgs.etwSessionId, false, null, false);
2848                     }
2849 #endif // FEATURE_ACTIVITYSAMPLING
2850 
2851                     if (!commandArgs.enable)
2852                     {
2853                         // If we are disabling, maybe we can turn on 'quick checks' to filter
2854                         // quickly.  These are all just optimizations (since later checks will still filter)
2855 
2856 #if FEATURE_ACTIVITYSAMPLING
2857                         // Turn off (and forget) any information about Activity Tracing.
2858                         if (commandArgs.listener == null)
2859                         {
2860                             // reset all filtering information for activity-tracing-aware sessions
2861                             for (int i = 0; i < SessionMask.MAX; ++i)
2862                             {
2863                                 EtwSession etwSession = m_etwSessionIdMap[i];
2864                                 if (etwSession != null)
2865                                     ActivityFilter.DisableFilter(ref etwSession.m_activityFilter, this);
2866                             }
2867                             m_activityFilteringForETWEnabled = new SessionMask(0);
2868                             m_curLiveSessions = new SessionMask(0);
2869                             // reset activity-tracing-aware sessions
2870                             if (m_etwSessionIdMap != null)
2871                                 for (int i = 0; i < SessionMask.MAX; ++i)
2872                                     m_etwSessionIdMap[i] = null;
2873                             // reset legacy sessions
2874                             if (m_legacySessions != null)
2875                                 m_legacySessions.Clear();
2876                         }
2877                         else
2878                         {
2879                             ActivityFilter.DisableFilter(ref commandArgs.listener.m_activityFilter, this);
2880                             commandArgs.dispatcher.m_activityFilteringEnabled = false;
2881                         }
2882 #endif // FEATURE_ACTIVITYSAMPLING
2883 
2884                         // There is a good chance EnabledForAnyListener are not as accurate as
2885                         // they could be, go ahead and get a better estimate.
2886                         for (int i = 0; i < m_eventData.Length; i++)
2887                         {
2888                             bool isEnabledForAnyListener = false;
2889                             for (EventDispatcher dispatcher = m_Dispatchers; dispatcher != null; dispatcher = dispatcher.m_Next)
2890                             {
2891                                 if (dispatcher.m_EventEnabled[i])
2892                                 {
2893                                     isEnabledForAnyListener = true;
2894                                     break;
2895                                 }
2896                             }
2897                             m_eventData[i].EnabledForAnyListener = isEnabledForAnyListener;
2898                         }
2899 
2900                         // If no events are enabled, disable the global enabled bit.
2901                         if (!AnyEventEnabled())
2902                         {
2903                             m_level = 0;
2904                             m_matchAnyKeyword = 0;
2905                             m_eventSourceEnabled = false;
2906                         }
2907                     }
2908 #if FEATURE_ACTIVITYSAMPLING
2909                     UpdateKwdTriggers(commandArgs.enable);
2910 #endif // FEATURE_ACTIVITYSAMPLING
2911                 }
2912                 else
2913                 {
2914 #if !FEATURE_PERFTRACING
2915                     if (commandArgs.Command == EventCommand.SendManifest)
2916                     {
2917                         // TODO: should we generate the manifest here if we hadn't already?
2918                         if (m_rawManifest != null)
2919                             SendManifest(m_rawManifest);
2920                     }
2921 #endif
2922 
2923                     // These are not used for non-update commands and thus should always be 'default' values
2924                     // Debug.Assert(enable == true);
2925                     // Debug.Assert(level == EventLevel.LogAlways);
2926                     // Debug.Assert(matchAnyKeyword == EventKeywords.None);
2927 
2928                     this.OnEventCommand(commandArgs);
2929                     var eventCommandCallback = m_eventCommandExecuted;
2930                     if (eventCommandCallback != null)
2931                         eventCommandCallback(this, commandArgs);
2932                 }
2933 
2934 #if FEATURE_ACTIVITYSAMPLING
2935                 if (m_completelyInited && (commandArgs.listener != null || shouldReport))
2936                 {
2937                     SessionMask m = SessionMask.FromId(commandArgs.perEventSourceSessionId);
2938                     ReportActivitySamplingInfo(commandArgs.listener, m);
2939                 }
2940 #endif // FEATURE_ACTIVITYSAMPLING
2941             }
2942             catch (Exception e)
2943             {
2944                 // When the ETW session is created after the EventSource has registered with the ETW system
2945                 // we can send any error messages here.
2946                 ReportOutOfBandMessage("ERROR: Exception in Command Processing for EventSource " + Name + ": " + e.Message, true);
2947                 // We never throw when doing a command.
2948             }
2949         }
2950 
2951 #if FEATURE_ACTIVITYSAMPLING
2952 
UpdateEtwSession( int sessionIdBit, int etwSessionId, bool bEnable, string activityFilters, bool participateInSampling)2953         internal void UpdateEtwSession(
2954             int sessionIdBit,
2955             int etwSessionId,
2956             bool bEnable,
2957             string activityFilters,
2958             bool participateInSampling)
2959         {
2960             if (sessionIdBit < SessionMask.MAX)
2961             {
2962                 // activity-tracing-aware etw session
2963                 if (bEnable)
2964                 {
2965                     var etwSession = EtwSession.GetEtwSession(etwSessionId, true);
2966                     ActivityFilter.UpdateFilter(ref etwSession.m_activityFilter, this, sessionIdBit, activityFilters);
2967                     m_etwSessionIdMap[sessionIdBit] = etwSession;
2968                     m_activityFilteringForETWEnabled[sessionIdBit] = participateInSampling;
2969                 }
2970                 else
2971                 {
2972                     var etwSession = EtwSession.GetEtwSession(etwSessionId);
2973                     m_etwSessionIdMap[sessionIdBit] = null;
2974                     m_activityFilteringForETWEnabled[sessionIdBit] = false;
2975                     if (etwSession != null)
2976                     {
2977                         ActivityFilter.DisableFilter(ref etwSession.m_activityFilter, this);
2978                         // the ETW session is going away; remove it from the global list
2979                         EtwSession.RemoveEtwSession(etwSession);
2980                     }
2981                 }
2982                 m_curLiveSessions[sessionIdBit] = bEnable;
2983             }
2984             else
2985             {
2986                 // legacy etw session
2987                 if (bEnable)
2988                 {
2989                     if (m_legacySessions == null)
2990                         m_legacySessions = new List<EtwSession>(8);
2991                     var etwSession = EtwSession.GetEtwSession(etwSessionId, true);
2992                     if (!m_legacySessions.Contains(etwSession))
2993                         m_legacySessions.Add(etwSession);
2994                 }
2995                 else
2996                 {
2997                     var etwSession = EtwSession.GetEtwSession(etwSessionId);
2998                     if (etwSession != null)
2999                     {
3000                         if (m_legacySessions != null)
3001                             m_legacySessions.Remove(etwSession);
3002                         // the ETW session is going away; remove it from the global list
3003                         EtwSession.RemoveEtwSession(etwSession);
3004                     }
3005                 }
3006             }
3007         }
3008 
ParseCommandArgs( IDictionary<string, string> commandArguments, out bool participateInSampling, out string activityFilters, out int sessionIdBit)3009         internal static bool ParseCommandArgs(
3010                         IDictionary<string, string> commandArguments,
3011                         out bool participateInSampling,
3012                         out string activityFilters,
3013                         out int sessionIdBit)
3014         {
3015             bool res = true;
3016             participateInSampling = false;
3017             string activityFilterString;
3018             if (commandArguments.TryGetValue("ActivitySamplingStartEvent", out activityFilters))
3019             {
3020                 // if a start event is specified default the event source to participate in sampling
3021                 participateInSampling = true;
3022             }
3023 
3024             if (commandArguments.TryGetValue("ActivitySampling", out activityFilterString))
3025             {
3026                 if (string.Compare(activityFilterString, "false", StringComparison.OrdinalIgnoreCase) == 0 ||
3027                     activityFilterString == "0")
3028                     participateInSampling = false;
3029                 else
3030                     participateInSampling = true;
3031             }
3032 
3033             string sSessionKwd;
3034             int sessionKwd = -1;
3035             if (!commandArguments.TryGetValue("EtwSessionKeyword", out sSessionKwd) ||
3036                 !int.TryParse(sSessionKwd, out sessionKwd) ||
3037                 sessionKwd < SessionMask.SHIFT_SESSION_TO_KEYWORD ||
3038                 sessionKwd >= SessionMask.SHIFT_SESSION_TO_KEYWORD + SessionMask.MAX)
3039             {
3040                 sessionIdBit = -1;
3041                 res = false;
3042             }
3043             else
3044             {
3045                 sessionIdBit = sessionKwd - SessionMask.SHIFT_SESSION_TO_KEYWORD;
3046             }
3047             return res;
3048         }
3049 
UpdateKwdTriggers(bool enable)3050         internal void UpdateKwdTriggers(bool enable)
3051         {
3052             if (enable)
3053             {
3054                 // recompute m_keywordTriggers
3055                 ulong gKeywords = unchecked((ulong)m_matchAnyKeyword);
3056                 if (gKeywords == 0)
3057                     gKeywords = 0xFFFFffffFFFFffff;
3058 
3059                 m_keywordTriggers = 0;
3060                 for (int sessId = 0; sessId < SessionMask.MAX; ++sessId)
3061                 {
3062                     EtwSession etwSession = m_etwSessionIdMap[sessId];
3063                     if (etwSession == null)
3064                         continue;
3065 
3066                     ActivityFilter activityFilter = etwSession.m_activityFilter;
3067                     ActivityFilter.UpdateKwdTriggers(activityFilter, m_guid, this, unchecked((EventKeywords)gKeywords));
3068                 }
3069             }
3070             else
3071             {
3072                 m_keywordTriggers = 0;
3073             }
3074         }
3075 
3076 #endif // FEATURE_ACTIVITYSAMPLING
3077 
3078         /// <summary>
3079         /// If 'value is 'true' then set the eventSource so that 'dispatcher' will receive event with the eventId
3080         /// of 'eventId.  If value is 'false' disable the event for that dispatcher.   If 'eventId' is out of
3081         /// range return false, otherwise true.
3082         /// </summary>
EnableEventForDispatcher(EventDispatcher dispatcher, int eventId, bool value)3083         internal bool EnableEventForDispatcher(EventDispatcher dispatcher, int eventId, bool value)
3084         {
3085             if (dispatcher == null)
3086             {
3087                 if (eventId >= m_eventData.Length)
3088                     return false;
3089 #if FEATURE_MANAGED_ETW
3090                 if (m_provider != null)
3091                     m_eventData[eventId].EnabledForETW = value;
3092 #endif
3093             }
3094             else
3095             {
3096                 if (eventId >= dispatcher.m_EventEnabled.Length)
3097                     return false;
3098                 dispatcher.m_EventEnabled[eventId] = value;
3099                 if (value)
3100                     m_eventData[eventId].EnabledForAnyListener = true;
3101             }
3102             return true;
3103         }
3104 
3105         /// <summary>
3106         /// Returns true if any event at all is on.
3107         /// </summary>
AnyEventEnabled()3108         private bool AnyEventEnabled()
3109         {
3110             for (int i = 0; i < m_eventData.Length; i++)
3111                 if (m_eventData[i].EnabledForETW || m_eventData[i].EnabledForAnyListener)
3112                     return true;
3113             return false;
3114         }
3115 
3116         private bool IsDisposed
3117         {
3118             get { return m_eventSourceDisposed; }
3119         }
3120 
EnsureDescriptorsInitialized()3121         private void EnsureDescriptorsInitialized()
3122         {
3123 #if !ES_BUILD_STANDALONE
3124             Debug.Assert(Monitor.IsEntered(EventListener.EventListenersLock));
3125 #endif
3126             if (m_eventData == null)
3127             {
3128                 Guid eventSourceGuid = Guid.Empty;
3129                 string eventSourceName = null;
3130                 EventMetadata[] eventData = null;
3131                 byte[] manifest = null;
3132 
3133                 // Try the GetMetadata provided by the ILTransform in ProjectN. The default sets all to null, and in that case we fall back
3134                 // to the reflection approach.
3135                 GetMetadata(out eventSourceGuid, out eventSourceName, out eventData, out manifest);
3136 
3137                 if (eventSourceGuid.Equals(Guid.Empty) || eventSourceName == null || eventData == null || manifest == null)
3138                 {
3139                     // GetMetadata failed, so we have to set it via reflection.
3140                     Debug.Assert(m_rawManifest == null);
3141 
3142                     m_rawManifest = CreateManifestAndDescriptors(this.GetType(), Name, this);
3143                     Debug.Assert(m_eventData != null);
3144 
3145                 }
3146                 else
3147                 {
3148                     // GetMetadata worked, so set the fields as appropriate.
3149                     m_name = eventSourceName;
3150                     m_guid = eventSourceGuid;
3151                     m_eventData = eventData;
3152                     m_rawManifest = manifest;
3153                 }
3154                 // TODO Enforce singleton pattern
3155                 foreach (WeakReference eventSourceRef in EventListener.s_EventSources)
3156                 {
3157                     EventSource eventSource = eventSourceRef.Target as EventSource;
3158                     if (eventSource != null && eventSource.Guid == m_guid && !eventSource.IsDisposed)
3159                     {
3160                         if (eventSource != this)
3161                         {
3162                             throw new ArgumentException(SR.Format(SR.EventSource_EventSourceGuidInUse, m_guid));
3163                         }
3164                     }
3165                 }
3166 
3167                 // Make certain all dispatchers also have their arrays initialized
3168                 EventDispatcher dispatcher = m_Dispatchers;
3169                 while (dispatcher != null)
3170                 {
3171                     if (dispatcher.m_EventEnabled == null)
3172                         dispatcher.m_EventEnabled = new bool[m_eventData.Length];
3173                     dispatcher = dispatcher.m_Next;
3174                 }
3175 #if FEATURE_PERFTRACING
3176                 // Initialize the EventPipe event handles.
3177                 DefineEventPipeEvents();
3178 #endif
3179             }
3180             if (s_currentPid == 0)
3181             {
3182 #if ES_BUILD_STANDALONE && !ES_BUILD_PCL && !CORECLR
3183                 // for non-BCL EventSource we must assert SecurityPermission
3184                 new SecurityPermission(PermissionState.Unrestricted).Assert();
3185 #endif
3186                 s_currentPid = Win32Native.GetCurrentProcessId();
3187             }
3188         }
3189 
3190         // Send out the ETW manifest XML out to ETW
3191         // Today, we only send the manifest to ETW, custom listeners don't get it.
SendManifest(byte[] rawManifest)3192         private unsafe bool SendManifest(byte[] rawManifest)
3193         {
3194             bool success = true;
3195 
3196             if (rawManifest == null)
3197                 return false;
3198 
3199             Debug.Assert(!SelfDescribingEvents);
3200 
3201 #if FEATURE_MANAGED_ETW
3202             fixed (byte* dataPtr = rawManifest)
3203             {
3204                 // we don't want the manifest to show up in the event log channels so we specify as keywords
3205                 // everything but the first 8 bits (reserved for the 8 channels)
3206                 var manifestDescr = new EventDescriptor(0xFFFE, 1, 0, 0, 0xFE, 0xFFFE, 0x00ffFFFFffffFFFF);
3207                 ManifestEnvelope envelope = new ManifestEnvelope();
3208 
3209                 envelope.Format = ManifestEnvelope.ManifestFormats.SimpleXmlFormat;
3210                 envelope.MajorVersion = 1;
3211                 envelope.MinorVersion = 0;
3212                 envelope.Magic = 0x5B;              // An unusual number that can be checked for consistency.
3213                 int dataLeft = rawManifest.Length;
3214                 envelope.ChunkNumber = 0;
3215 
3216                 EventProvider.EventData* dataDescrs = stackalloc EventProvider.EventData[2];
3217 
3218                 dataDescrs[0].Ptr = (ulong)&envelope;
3219                 dataDescrs[0].Size = (uint)sizeof(ManifestEnvelope);
3220                 dataDescrs[0].Reserved = 0;
3221 
3222                 dataDescrs[1].Ptr = (ulong)dataPtr;
3223                 dataDescrs[1].Reserved = 0;
3224 
3225                 int chunkSize = ManifestEnvelope.MaxChunkSize;
3226                 TRY_AGAIN_WITH_SMALLER_CHUNK_SIZE:
3227                 envelope.TotalChunks = (ushort)((dataLeft + (chunkSize - 1)) / chunkSize);
3228                 while (dataLeft > 0)
3229                 {
3230                     dataDescrs[1].Size = (uint)Math.Min(dataLeft, chunkSize);
3231                     if (m_provider != null)
3232                     {
3233                         if (!m_provider.WriteEvent(ref manifestDescr, IntPtr.Zero, null, null, 2, (IntPtr)dataDescrs))
3234                         {
3235                             // Turns out that if users set the BufferSize to something less than 64K then WriteEvent
3236                             // can fail.   If we get this failure on the first chunk try again with something smaller
3237                             // The smallest BufferSize is 1K so if we get to 256 (to account for envelope overhead), we can give up making it smaller.
3238                             if (EventProvider.GetLastWriteEventError() == EventProvider.WriteEventErrorCode.EventTooBig)
3239                             {
3240                                 if (envelope.ChunkNumber == 0 && chunkSize > 256)
3241                                 {
3242                                     chunkSize = chunkSize / 2;
3243                                     goto TRY_AGAIN_WITH_SMALLER_CHUNK_SIZE;
3244                                 }
3245                             }
3246                             success = false;
3247                             if (ThrowOnEventWriteErrors)
3248                                 ThrowEventSourceException("SendManifest");
3249                             break;
3250                         }
3251                     }
3252                     dataLeft -= chunkSize;
3253                     dataDescrs[1].Ptr += (uint)chunkSize;
3254                     envelope.ChunkNumber++;
3255 
3256                     // For large manifests we want to not overflow any receiver's buffer. Most manifests will fit within
3257                     // 5 chunks, so only the largest manifests will hit the pause.
3258                     if ((envelope.ChunkNumber % 5) == 0)
3259                     {
3260 #if ES_BUILD_STANDALONE
3261                         Thread.Sleep(15);
3262 #else
3263                         RuntimeThread.Sleep(15);
3264 #endif
3265                     }
3266                 }
3267             }
3268 #endif // FEATURE_MANAGED_ETW
3269             return success;
3270         }
3271 
3272 #if (ES_BUILD_PCL)
GetCustomAttributeHelper(Type type, Type attributeType, EventManifestOptions flags = EventManifestOptions.None)3273         internal static Attribute GetCustomAttributeHelper(Type type, Type attributeType, EventManifestOptions flags = EventManifestOptions.None)
3274         {
3275             return GetCustomAttributeHelper(type.GetTypeInfo(), attributeType, flags);
3276         }
3277 #endif
3278 
3279         // Helper to deal with the fact that the type we are reflecting over might be loaded in the ReflectionOnly context.
3280         // When that is the case, we have the build the custom assemblies on a member by hand.
GetCustomAttributeHelper(MemberInfo member, Type attributeType, EventManifestOptions flags = EventManifestOptions.None)3281         internal static Attribute GetCustomAttributeHelper(MemberInfo member, Type attributeType, EventManifestOptions flags = EventManifestOptions.None)
3282         {
3283 #if !ES_BUILD_PN
3284             // On ProjectN, ReflectionOnly() always equals false.  AllowEventSourceOverride is an option that allows either Microsoft.Diagnostics.Tracing or
3285             // System.Diagnostics.Tracing EventSource to be considered valid.  This should not mattter anywhere but in Microsoft.Diagnostics.Tracing (nuget package).
3286             if (!member.Module.Assembly.ReflectionOnly() && (flags & EventManifestOptions.AllowEventSourceOverride) == 0)
3287 #endif // !ES_BUILD_PN
3288             {
3289                 // Let the runtime to the work for us, since we can execute code in this context.
3290                 Attribute firstAttribute = null;
3291                 foreach (var attribute in member.GetCustomAttributes(attributeType, false))
3292                 {
3293                     firstAttribute = (Attribute)attribute;
3294                     break;
3295                 }
3296                 return firstAttribute;
3297             }
3298 
3299 #if (!ES_BUILD_PCL && !ES_BUILD_PN)
3300             // In the reflection only context, we have to do things by hand.
3301             string fullTypeNameToFind = attributeType.FullName;
3302 
3303 #if EVENT_SOURCE_LEGACY_NAMESPACE_SUPPORT
3304             fullTypeNameToFind = fullTypeNameToFind.Replace("System.Diagnostics.Eventing", "System.Diagnostics.Tracing");
3305 #endif
3306 
3307             foreach (CustomAttributeData data in CustomAttributeData.GetCustomAttributes(member))
3308             {
3309                 if (AttributeTypeNamesMatch(attributeType, data.Constructor.ReflectedType))
3310                 {
3311                     Attribute attr = null;
3312 
3313                     Debug.Assert(data.ConstructorArguments.Count <= 1);
3314 
3315                     if (data.ConstructorArguments.Count == 1)
3316                     {
3317                         attr = (Attribute)Activator.CreateInstance(attributeType, new object[] { data.ConstructorArguments[0].Value });
3318                     }
3319                     else if (data.ConstructorArguments.Count == 0)
3320                     {
3321                         attr = (Attribute)Activator.CreateInstance(attributeType);
3322                     }
3323 
3324                     if (attr != null)
3325                     {
3326                         Type t = attr.GetType();
3327 
3328                         foreach (CustomAttributeNamedArgument namedArgument in data.NamedArguments)
3329                         {
3330                             PropertyInfo p = t.GetProperty(namedArgument.MemberInfo.Name, BindingFlags.Public | BindingFlags.Instance);
3331                             object value = namedArgument.TypedValue.Value;
3332 
3333                             if (p.PropertyType.IsEnum)
3334                             {
3335                                 value = Enum.Parse(p.PropertyType, value.ToString());
3336                             }
3337 
3338                             p.SetValue(attr, value, null);
3339                         }
3340 
3341                         return attr;
3342                     }
3343                 }
3344             }
3345 
3346             return null;
3347 #else // ES_BUILD_PCL && ES_BUILD_PN
3348             // Don't use nameof here because the resource doesn't exist on some platforms, which results in a compilation error.
3349             throw new ArgumentException("EventSource_PCLPlatformNotSupportedReflection", "EventSource");
3350 #endif
3351         }
3352 
3353         /// <summary>
3354         /// Evaluates if two related "EventSource"-domain types should be considered the same
3355         /// </summary>
3356         /// <param name="attributeType">The attribute type in the load context - it's associated with the running
3357         /// EventSource type. This type may be different fromt he base type of the user-defined EventSource.</param>
3358         /// <param name="reflectedAttributeType">The attribute type in the reflection context - it's associated with
3359         /// the user-defined EventSource, and is in the same assembly as the eventSourceType passed to
3360         /// </param>
3361         /// <returns>True - if the types should be considered equivalent, False - otherwise</returns>
AttributeTypeNamesMatch(Type attributeType, Type reflectedAttributeType)3362         private static bool AttributeTypeNamesMatch(Type attributeType, Type reflectedAttributeType)
3363         {
3364             return
3365                 // are these the same type?
3366                 attributeType == reflectedAttributeType ||
3367                 // are the full typenames equal?
3368                 string.Equals(attributeType.FullName, reflectedAttributeType.FullName, StringComparison.Ordinal) ||
3369                     // are the typenames equal and the namespaces under "Diagnostics.Tracing" (typically
3370                     // either Microsoft.Diagnostics.Tracing or System.Diagnostics.Tracing)?
3371                     string.Equals(attributeType.Name, reflectedAttributeType.Name, StringComparison.Ordinal) &&
3372                     attributeType.Namespace.EndsWith("Diagnostics.Tracing", StringComparison.Ordinal) &&
3373                     (reflectedAttributeType.Namespace.EndsWith("Diagnostics.Tracing", StringComparison.Ordinal)
3374 #if EVENT_SOURCE_LEGACY_NAMESPACE_SUPPORT
3375                      || reflectedAttributeType.Namespace.EndsWith("Diagnostics.Eventing", StringComparison.Ordinal)
3376 #endif
3377 );
3378         }
3379 
GetEventSourceBaseType(Type eventSourceType, bool allowEventSourceOverride, bool reflectionOnly)3380         private static Type GetEventSourceBaseType(Type eventSourceType, bool allowEventSourceOverride, bool reflectionOnly)
3381         {
3382             // return false for "object" and interfaces
3383             if (eventSourceType.BaseType() == null)
3384                 return null;
3385 
3386             // now go up the inheritance chain until hitting a concrete type ("object" at worse)
3387             do
3388             {
3389                 eventSourceType = eventSourceType.BaseType();
3390             }
3391             while (eventSourceType != null && eventSourceType.IsAbstract());
3392 
3393             if (eventSourceType != null)
3394             {
3395                 if (!allowEventSourceOverride)
3396                 {
3397                     if (reflectionOnly && eventSourceType.FullName != typeof(EventSource).FullName ||
3398                         !reflectionOnly && eventSourceType != typeof(EventSource))
3399                         return null;
3400                 }
3401                 else
3402                 {
3403                     if (eventSourceType.Name != "EventSource")
3404                         return null;
3405                 }
3406             }
3407             return eventSourceType;
3408         }
3409 
3410         // Use reflection to look at the attributes of a class, and generate a manifest for it (as UTF8) and
3411         // return the UTF8 bytes.  It also sets up the code:EventData structures needed to dispatch events
3412         // at run time.  'source' is the event source to place the descriptors.  If it is null,
3413         // then the descriptors are not creaed, and just the manifest is generated.
CreateManifestAndDescriptors(Type eventSourceType, string eventSourceDllName, EventSource source, EventManifestOptions flags = EventManifestOptions.None)3414         private static byte[] CreateManifestAndDescriptors(Type eventSourceType, string eventSourceDllName, EventSource source,
3415             EventManifestOptions flags = EventManifestOptions.None)
3416         {
3417             ManifestBuilder manifest = null;
3418             bool bNeedsManifest = source != null ? !source.SelfDescribingEvents : true;
3419             Exception exception = null; // exception that might get raised during validation b/c we couldn't/didn't recover from a previous error
3420             byte[] res = null;
3421 
3422             if (eventSourceType.IsAbstract() && (flags & EventManifestOptions.Strict) == 0)
3423                 return null;
3424 
3425 #if DEBUG && ES_BUILD_STANDALONE && TEST_SUPPORT
3426             TestSupport.TestHooks.MaybeThrow(eventSourceType,
3427                                         TestSupport.Category.ManifestError,
3428                                         "EventSource_CreateManifestAndDescriptors",
3429                                         new ArgumentException("EventSource_CreateManifestAndDescriptors"));
3430 #endif
3431 
3432             try
3433             {
3434                 MethodInfo[] methods = eventSourceType.GetMethods(BindingFlags.DeclaredOnly | BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance);
3435                 EventAttribute defaultEventAttribute;
3436                 int eventId = 1;        // The number given to an event that does not have a explicitly given ID.
3437                 EventMetadata[] eventData = null;
3438                 Dictionary<string, string> eventsByName = null;
3439                 if (source != null || (flags & EventManifestOptions.Strict) != 0)
3440                 {
3441                     eventData = new EventMetadata[methods.Length + 1];
3442                     eventData[0].Name = "";         // Event 0 is the 'write messages string' event, and has an empty name.
3443                 }
3444 
3445                 // See if we have localization information.
3446                 ResourceManager resources = null;
3447                 EventSourceAttribute eventSourceAttrib = (EventSourceAttribute)GetCustomAttributeHelper(eventSourceType, typeof(EventSourceAttribute), flags);
3448                 if (eventSourceAttrib != null && eventSourceAttrib.LocalizationResources != null)
3449                     resources = new ResourceManager(eventSourceAttrib.LocalizationResources, eventSourceType.Assembly());
3450 
3451                 manifest = new ManifestBuilder(GetName(eventSourceType, flags), GetGuid(eventSourceType), eventSourceDllName,
3452                                                resources, flags);
3453 
3454                 // Add an entry unconditionally for event ID 0 which will be for a string message.
3455                 manifest.StartEvent("EventSourceMessage", new EventAttribute(0) { Level = EventLevel.LogAlways, Task = (EventTask)0xFFFE });
3456                 manifest.AddEventParameter(typeof(string), "message");
3457                 manifest.EndEvent();
3458 
3459                 // eventSourceType must be sealed and must derive from this EventSource
3460                 if ((flags & EventManifestOptions.Strict) != 0)
3461                 {
3462                     bool typeMatch = GetEventSourceBaseType(eventSourceType, (flags & EventManifestOptions.AllowEventSourceOverride) != 0, eventSourceType.Assembly().ReflectionOnly()) != null;
3463 
3464                     if (!typeMatch)
3465                     {
3466                         manifest.ManifestError(SR.EventSource_TypeMustDeriveFromEventSource);
3467                     }
3468                     if (!eventSourceType.IsAbstract() && !eventSourceType.IsSealed())
3469                     {
3470                         manifest.ManifestError(SR.EventSource_TypeMustBeSealedOrAbstract);
3471                     }
3472                 }
3473 
3474                 // Collect task, opcode, keyword and channel information
3475 #if FEATURE_MANAGED_ETW_CHANNELS && FEATURE_ADVANCED_MANAGED_ETW_CHANNELS
3476                 foreach (var providerEnumKind in new string[] { "Keywords", "Tasks", "Opcodes", "Channels" })
3477 #else
3478                 foreach (var providerEnumKind in new string[] { "Keywords", "Tasks", "Opcodes" })
3479 #endif
3480                 {
3481                     Type nestedType = eventSourceType.GetNestedType(providerEnumKind);
3482                     if (nestedType != null)
3483                     {
3484                         if (eventSourceType.IsAbstract())
3485                         {
3486                             manifest.ManifestError(SR.Format(SR.EventSource_AbstractMustNotDeclareKTOC, nestedType.Name));
3487                         }
3488                         else
3489                         {
3490                             foreach (FieldInfo staticField in nestedType.GetFields(BindingFlags.DeclaredOnly | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static))
3491                             {
3492                                 AddProviderEnumKind(manifest, staticField, providerEnumKind);
3493                             }
3494                         }
3495                     }
3496                 }
3497                 // ensure we have keywords for the session-filtering reserved bits
3498                 {
3499                     manifest.AddKeyword("Session3", (long)0x1000 << 32);
3500                     manifest.AddKeyword("Session2", (long)0x2000 << 32);
3501                     manifest.AddKeyword("Session1", (long)0x4000 << 32);
3502                     manifest.AddKeyword("Session0", (long)0x8000 << 32);
3503                 }
3504 
3505                 if (eventSourceType != typeof(EventSource))
3506                 {
3507                     for (int i = 0; i < methods.Length; i++)
3508                     {
3509                         MethodInfo method = methods[i];
3510                         ParameterInfo[] args = method.GetParameters();
3511 
3512                         // Get the EventDescriptor (from the Custom attributes)
3513                         EventAttribute eventAttribute = (EventAttribute)GetCustomAttributeHelper(method, typeof(EventAttribute), flags);
3514 
3515                         // Compat: until v4.5.1 we ignored any non-void returning methods as well as virtual methods for
3516                         // the only reason of limiting the number of methods considered to be events. This broke a common
3517                         // design of having event sources implement specific interfaces. To fix this in a compatible way
3518                         // we will now allow both non-void returning and virtual methods to be Event methods, as long
3519                         // as they are marked with the [Event] attribute
3520                         if (/* method.IsVirtual || */ method.IsStatic)
3521                         {
3522                             continue;
3523                         }
3524 
3525                         if (eventSourceType.IsAbstract())
3526                         {
3527                             if (eventAttribute != null)
3528                             {
3529                                 manifest.ManifestError(SR.Format(SR.EventSource_AbstractMustNotDeclareEventMethods, method.Name, eventAttribute.EventId));
3530                             }
3531                             continue;
3532                         }
3533                         else if (eventAttribute == null)
3534                         {
3535                             // Methods that don't return void can't be events, if they're NOT marked with [Event].
3536                             // (see Compat comment above)
3537                             if (method.ReturnType != typeof(void))
3538                             {
3539                                 continue;
3540                             }
3541 
3542                             // Continue to ignore virtual methods if they do NOT have the [Event] attribute
3543                             // (see Compat comment above)
3544                             if (method.IsVirtual)
3545                             {
3546                                 continue;
3547                             }
3548 
3549                             // If we explicitly mark the method as not being an event, then honor that.
3550                             if (GetCustomAttributeHelper(method, typeof(NonEventAttribute), flags) != null)
3551                                 continue;
3552 
3553                             defaultEventAttribute = new EventAttribute(eventId);
3554                             eventAttribute = defaultEventAttribute;
3555                         }
3556                         else if (eventAttribute.EventId <= 0)
3557                         {
3558                             manifest.ManifestError(SR.Format(SR.EventSource_NeedPositiveId, method.Name), true);
3559                             continue;   // don't validate anything else for this event
3560                         }
3561                         if (method.Name.LastIndexOf('.') >= 0)
3562                         {
3563                             manifest.ManifestError(SR.Format(SR.EventSource_EventMustNotBeExplicitImplementation, method.Name, eventAttribute.EventId));
3564                         }
3565 
3566                         eventId++;
3567                         string eventName = method.Name;
3568 
3569                         if (eventAttribute.Opcode == EventOpcode.Info)      // We are still using the default opcode.
3570                         {
3571                             // By default pick a task ID derived from the EventID, starting with the highest task number and working back
3572                             bool noTask = (eventAttribute.Task == EventTask.None);
3573                             if (noTask)
3574                                 eventAttribute.Task = (EventTask)(0xFFFE - eventAttribute.EventId);
3575 
3576                             // Unless we explicitly set the opcode to Info (to override the auto-generate of Start or Stop opcodes,
3577                             // pick a default opcode based on the event name (either Info or start or stop if the name ends with that suffix).
3578                             if (!eventAttribute.IsOpcodeSet)
3579                                 eventAttribute.Opcode = GetOpcodeWithDefault(EventOpcode.Info, eventName);
3580 
3581                             // Make the stop opcode have the same task as the start opcode.
3582                             if (noTask)
3583                             {
3584                                 if (eventAttribute.Opcode == EventOpcode.Start)
3585                                 {
3586                                     string taskName = eventName.Substring(0, eventName.Length - s_ActivityStartSuffix.Length); // Remove the Stop suffix to get the task name
3587                                     if (string.Compare(eventName, 0, taskName, 0, taskName.Length) == 0 &&
3588                                         string.Compare(eventName, taskName.Length, s_ActivityStartSuffix, 0, Math.Max(eventName.Length - taskName.Length, s_ActivityStartSuffix.Length)) == 0)
3589                                     {
3590                                         // Add a task that is just the task name for the start event.   This suppress the auto-task generation
3591                                         // That would otherwise happen (and create 'TaskName'Start as task name rather than just 'TaskName'
3592                                         manifest.AddTask(taskName, (int)eventAttribute.Task);
3593                                     }
3594                                 }
3595                                 else if (eventAttribute.Opcode == EventOpcode.Stop)
3596                                 {
3597                                     // Find the start associated with this stop event.  We require start to be immediately before the stop
3598                                     int startEventId = eventAttribute.EventId - 1;
3599                                     if (eventData != null && startEventId < eventData.Length)
3600                                     {
3601                                         Debug.Assert(0 <= startEventId);                // Since we reserve id 0, we know that id-1 is <= 0
3602                                         EventMetadata startEventMetadata = eventData[startEventId];
3603 
3604                                         // If you remove the Stop and add a Start does that name match the Start Event's Name?
3605                                         // Ideally we would throw an error
3606                                         string taskName = eventName.Substring(0, eventName.Length - s_ActivityStopSuffix.Length); // Remove the Stop suffix to get the task name
3607                                         if (startEventMetadata.Descriptor.Opcode == (byte)EventOpcode.Start &&
3608                                             string.Compare(startEventMetadata.Name, 0, taskName, 0, taskName.Length) == 0 &&
3609                                             string.Compare(startEventMetadata.Name, taskName.Length, s_ActivityStartSuffix, 0, Math.Max(startEventMetadata.Name.Length - taskName.Length, s_ActivityStartSuffix.Length)) == 0)
3610                                         {
3611 
3612                                             // Make the stop event match the start event
3613                                             eventAttribute.Task = (EventTask)startEventMetadata.Descriptor.Task;
3614                                             noTask = false;
3615                                         }
3616                                     }
3617                                     if (noTask && (flags & EventManifestOptions.Strict) != 0)        // Throw an error if we can compatibly.
3618                                     {
3619                                         throw new ArgumentException(SR.EventSource_StopsFollowStarts);
3620                                     }
3621                                 }
3622                             }
3623                         }
3624 
3625                         bool hasRelatedActivityID = RemoveFirstArgIfRelatedActivityId(ref args);
3626                         if (!(source != null && source.SelfDescribingEvents))
3627                         {
3628                             manifest.StartEvent(eventName, eventAttribute);
3629                             for (int fieldIdx = 0; fieldIdx < args.Length; fieldIdx++)
3630                             {
3631                                 manifest.AddEventParameter(args[fieldIdx].ParameterType, args[fieldIdx].Name);
3632                             }
3633                             manifest.EndEvent();
3634                         }
3635 
3636                         if (source != null || (flags & EventManifestOptions.Strict) != 0)
3637                         {
3638                             // Do checking for user errors (optional, but not a big deal so we do it).
3639                             DebugCheckEvent(ref eventsByName, eventData, method, eventAttribute, manifest, flags);
3640 
3641 #if FEATURE_MANAGED_ETW_CHANNELS
3642                             // add the channel keyword for Event Viewer channel based filters. This is added for creating the EventDescriptors only
3643                             // and is not required for the manifest
3644                             if (eventAttribute.Channel != EventChannel.None)
3645                             {
3646                                 unchecked
3647                                 {
3648                                     eventAttribute.Keywords |= (EventKeywords)manifest.GetChannelKeyword(eventAttribute.Channel, (ulong)eventAttribute.Keywords);
3649                                 }
3650                             }
3651 #endif
3652                             string eventKey = "event_" + eventName;
3653                             string msg = manifest.GetLocalizedMessage(eventKey, CultureInfo.CurrentUICulture, etwFormat: false);
3654                             // overwrite inline message with the localized message
3655                             if (msg != null) eventAttribute.Message = msg;
3656 
3657                             AddEventDescriptor(ref eventData, eventName, eventAttribute, args, hasRelatedActivityID);
3658                         }
3659                     }
3660                 }
3661 
3662                 // Tell the TraceLogging stuff where to start allocating its own IDs.
3663                 NameInfo.ReserveEventIDsBelow(eventId);
3664 
3665                 if (source != null)
3666                 {
3667                     TrimEventDescriptors(ref eventData);
3668                     source.m_eventData = eventData;     // officially initialize it. We do this at most once (it is racy otherwise).
3669 #if FEATURE_MANAGED_ETW_CHANNELS
3670                     source.m_channelData = manifest.GetChannelData();
3671 #endif
3672                 }
3673 
3674                 // if this is an abstract event source we've already performed all the validation we can
3675                 if (!eventSourceType.IsAbstract() && (source == null || !source.SelfDescribingEvents))
3676                 {
3677                     bNeedsManifest = (flags & EventManifestOptions.OnlyIfNeededForRegistration) == 0
3678 #if FEATURE_MANAGED_ETW_CHANNELS
3679                                             || manifest.GetChannelData().Length > 0
3680 #endif
3681 ;
3682 
3683                     // if the manifest is not needed and we're not requested to validate the event source return early
3684                     if (!bNeedsManifest && (flags & EventManifestOptions.Strict) == 0)
3685                         return null;
3686 
3687                     res = manifest.CreateManifest();
3688                 }
3689             }
3690             catch (Exception e)
3691             {
3692                 // if this is a runtime manifest generation let the exception propagate
3693                 if ((flags & EventManifestOptions.Strict) == 0)
3694                     throw;
3695                 // else store it to include it in the Argument exception we raise below
3696                 exception = e;
3697             }
3698 
3699             if ((flags & EventManifestOptions.Strict) != 0 && (manifest.Errors.Count > 0 || exception != null))
3700             {
3701                 string msg = String.Empty;
3702                 if (manifest.Errors.Count > 0)
3703                 {
3704                     bool firstError = true;
3705                     foreach (string error in manifest.Errors)
3706                     {
3707                         if (!firstError)
3708                             msg += Environment.NewLine;
3709                         firstError = false;
3710                         msg += error;
3711                     }
3712                 }
3713                 else
3714                     msg = "Unexpected error: " + exception.Message;
3715 
3716                 throw new ArgumentException(msg, exception);
3717             }
3718 
3719             return bNeedsManifest ? res : null;
3720         }
3721 
RemoveFirstArgIfRelatedActivityId(ref ParameterInfo[] args)3722         private static bool RemoveFirstArgIfRelatedActivityId(ref ParameterInfo[] args)
3723         {
3724             // If the first parameter is (case insensitive) 'relatedActivityId' then skip it.
3725             if (args.Length > 0 && args[0].ParameterType == typeof(Guid) &&
3726                 string.Compare(args[0].Name, "relatedActivityId", StringComparison.OrdinalIgnoreCase) == 0)
3727             {
3728                 var newargs = new ParameterInfo[args.Length - 1];
3729                 Array.Copy(args, 1, newargs, 0, args.Length - 1);
3730                 args = newargs;
3731 
3732                 return true;
3733             }
3734 
3735             return false;
3736         }
3737 
3738         // adds a enumeration (keyword, opcode, task or channel) represented by 'staticField'
3739         // to the manifest.
AddProviderEnumKind(ManifestBuilder manifest, FieldInfo staticField, string providerEnumKind)3740         private static void AddProviderEnumKind(ManifestBuilder manifest, FieldInfo staticField, string providerEnumKind)
3741         {
3742             bool reflectionOnly = staticField.Module.Assembly.ReflectionOnly();
3743             Type staticFieldType = staticField.FieldType;
3744             if (!reflectionOnly && (staticFieldType == typeof(EventOpcode)) || AttributeTypeNamesMatch(staticFieldType, typeof(EventOpcode)))
3745             {
3746                 if (providerEnumKind != "Opcodes") goto Error;
3747                 int value = (int)staticField.GetRawConstantValue();
3748                 manifest.AddOpcode(staticField.Name, value);
3749             }
3750             else if (!reflectionOnly && (staticFieldType == typeof(EventTask)) || AttributeTypeNamesMatch(staticFieldType, typeof(EventTask)))
3751             {
3752                 if (providerEnumKind != "Tasks") goto Error;
3753                 int value = (int)staticField.GetRawConstantValue();
3754                 manifest.AddTask(staticField.Name, value);
3755             }
3756             else if (!reflectionOnly && (staticFieldType == typeof(EventKeywords)) || AttributeTypeNamesMatch(staticFieldType, typeof(EventKeywords)))
3757             {
3758                 if (providerEnumKind != "Keywords") goto Error;
3759                 ulong value = unchecked((ulong)(long)staticField.GetRawConstantValue());
3760                 manifest.AddKeyword(staticField.Name, value);
3761             }
3762 #if FEATURE_MANAGED_ETW_CHANNELS && FEATURE_ADVANCED_MANAGED_ETW_CHANNELS
3763             else if (!reflectionOnly && (staticFieldType == typeof(EventChannel)) || AttributeTypeNamesMatch(staticFieldType, typeof(EventChannel)))
3764             {
3765                 if (providerEnumKind != "Channels") goto Error;
3766                 var channelAttribute = (EventChannelAttribute)GetCustomAttributeHelper(staticField, typeof(EventChannelAttribute));
3767                 manifest.AddChannel(staticField.Name, (byte)staticField.GetRawConstantValue(), channelAttribute);
3768             }
3769 #endif
3770             return;
3771             Error:
3772             manifest.ManifestError(SR.Format(SR.EventSource_EnumKindMismatch, staticField.Name, staticField.FieldType.Name, providerEnumKind));
3773         }
3774 
3775         // Helper used by code:CreateManifestAndDescriptors to add a code:EventData descriptor for a method
3776         // with the code:EventAttribute 'eventAttribute'.  resourceManger may be null in which case we populate it
3777         // it is populated if we need to look up message resources
AddEventDescriptor(ref EventMetadata[] eventData, string eventName, EventAttribute eventAttribute, ParameterInfo[] eventParameters, bool hasRelatedActivityID)3778         private static void AddEventDescriptor(ref EventMetadata[] eventData, string eventName,
3779                                 EventAttribute eventAttribute, ParameterInfo[] eventParameters,
3780                                 bool hasRelatedActivityID)
3781         {
3782             if (eventData == null || eventData.Length <= eventAttribute.EventId)
3783             {
3784                 EventMetadata[] newValues = new EventMetadata[Math.Max(eventData.Length + 16, eventAttribute.EventId + 1)];
3785                 Array.Copy(eventData, 0, newValues, 0, eventData.Length);
3786                 eventData = newValues;
3787             }
3788 
3789             eventData[eventAttribute.EventId].Descriptor = new EventDescriptor(
3790                     eventAttribute.EventId,
3791                     eventAttribute.Version,
3792 #if FEATURE_MANAGED_ETW_CHANNELS
3793                     (byte)eventAttribute.Channel,
3794 #else
3795                     (byte)0,
3796 #endif
3797                     (byte)eventAttribute.Level,
3798                     (byte)eventAttribute.Opcode,
3799                     (int)eventAttribute.Task,
3800                     unchecked((long)((ulong)eventAttribute.Keywords | SessionMask.All.ToEventKeywords())));
3801 
3802             eventData[eventAttribute.EventId].Tags = eventAttribute.Tags;
3803             eventData[eventAttribute.EventId].Name = eventName;
3804             eventData[eventAttribute.EventId].Parameters = eventParameters;
3805             eventData[eventAttribute.EventId].Message = eventAttribute.Message;
3806             eventData[eventAttribute.EventId].ActivityOptions = eventAttribute.ActivityOptions;
3807             eventData[eventAttribute.EventId].HasRelatedActivityID = hasRelatedActivityID;
3808             eventData[eventAttribute.EventId].EventHandle = IntPtr.Zero;
3809         }
3810 
3811         // Helper used by code:CreateManifestAndDescriptors that trims the m_eventData array to the correct
3812         // size after all event descriptors have been added.
TrimEventDescriptors(ref EventMetadata[] eventData)3813         private static void TrimEventDescriptors(ref EventMetadata[] eventData)
3814         {
3815             int idx = eventData.Length;
3816             while (0 < idx)
3817             {
3818                 --idx;
3819                 if (eventData[idx].Descriptor.EventId != 0)
3820                     break;
3821             }
3822             if (eventData.Length - idx > 2)      // allow one wasted slot.
3823             {
3824                 EventMetadata[] newValues = new EventMetadata[idx + 1];
3825                 Array.Copy(eventData, 0, newValues, 0, newValues.Length);
3826                 eventData = newValues;
3827             }
3828         }
3829 
3830         // Helper used by code:EventListener.AddEventSource and code:EventListener.EventListener
3831         // when a listener gets attached to a eventSource
AddListener(EventListener listener)3832         internal void AddListener(EventListener listener)
3833         {
3834             lock (EventListener.EventListenersLock)
3835             {
3836                 bool[] enabledArray = null;
3837                 if (m_eventData != null)
3838                     enabledArray = new bool[m_eventData.Length];
3839                 m_Dispatchers = new EventDispatcher(m_Dispatchers, enabledArray, listener);
3840                 listener.OnEventSourceCreated(this);
3841             }
3842         }
3843 
3844         // Helper used by code:CreateManifestAndDescriptors to find user mistakes like reusing an event
3845         // index for two distinct events etc.  Throws exceptions when it finds something wrong.
DebugCheckEvent(ref Dictionary<string, string> eventsByName, EventMetadata[] eventData, MethodInfo method, EventAttribute eventAttribute, ManifestBuilder manifest, EventManifestOptions options)3846         private static void DebugCheckEvent(ref Dictionary<string, string> eventsByName,
3847             EventMetadata[] eventData, MethodInfo method, EventAttribute eventAttribute,
3848             ManifestBuilder manifest, EventManifestOptions options)
3849         {
3850             int evtId = eventAttribute.EventId;
3851             string evtName = method.Name;
3852             int eventArg = GetHelperCallFirstArg(method);
3853             if (eventArg >= 0 && evtId != eventArg)
3854             {
3855                 manifest.ManifestError(SR.Format(SR.EventSource_MismatchIdToWriteEvent, evtName, evtId, eventArg), true);
3856             }
3857 
3858             if (evtId < eventData.Length && eventData[evtId].Descriptor.EventId != 0)
3859             {
3860                 manifest.ManifestError(SR.Format(SR.EventSource_EventIdReused, evtName, evtId, eventData[evtId].Name), true);
3861             }
3862 
3863             // We give a task to things if they don't have one.
3864             // TODO this is moderately expensive (N*N).   We probably should not even bother....
3865             Debug.Assert(eventAttribute.Task != EventTask.None || eventAttribute.Opcode != EventOpcode.Info);
3866             for (int idx = 0; idx < eventData.Length; ++idx)
3867             {
3868                 // skip unused Event IDs.
3869                 if (eventData[idx].Name == null)
3870                     continue;
3871 
3872                 if (eventData[idx].Descriptor.Task == (int)eventAttribute.Task && eventData[idx].Descriptor.Opcode == (int)eventAttribute.Opcode)
3873                 {
3874                     manifest.ManifestError(SR.Format(SR.EventSource_TaskOpcodePairReused,
3875                                             evtName, evtId, eventData[idx].Name, idx));
3876                     // If we are not strict stop on first error.   We have had problems with really large providers taking forever.  because of many errors.
3877                     if ((options & EventManifestOptions.Strict) == 0)
3878                         break;
3879                 }
3880             }
3881 
3882             // for non-default event opcodes the user must define a task!
3883             if (eventAttribute.Opcode != EventOpcode.Info)
3884             {
3885                 bool failure = false;
3886                 if (eventAttribute.Task == EventTask.None)
3887                     failure = true;
3888                 else
3889                 {
3890                     // If you have the auto-assigned Task, then you did not explicitly set one.
3891                     // This is OK for Start events because we have special logic to assign the task to a prefix derived from the event name
3892                     // But all other cases we want to catch the omission.
3893                     var autoAssignedTask = (EventTask)(0xFFFE - evtId);
3894                     if ((eventAttribute.Opcode != EventOpcode.Start && eventAttribute.Opcode != EventOpcode.Stop) && eventAttribute.Task == autoAssignedTask)
3895                         failure = true;
3896                 }
3897                 if (failure)
3898                 {
3899                     manifest.ManifestError(SR.Format(SR.EventSource_EventMustHaveTaskIfNonDefaultOpcode, evtName, evtId));
3900                 }
3901             }
3902 
3903             // If we ever want to enforce the rule: MethodName = TaskName + OpcodeName here's how:
3904             //  (the reason we don't is backwards compat and the need for handling this as a non-fatal error
3905             //  by eventRegister.exe)
3906             // taskName & opcodeName could be passed in by the caller which has opTab & taskTab handy
3907             // if (!(((int)eventAttribute.Opcode == 0 && evtName == taskName) || (evtName == taskName+opcodeName)))
3908             // {
3909             //     throw new WarningException(SR.EventSource_EventNameDoesNotEqualTaskPlusOpcode);
3910             // }
3911 
3912             if (eventsByName == null)
3913                 eventsByName = new Dictionary<string, string>();
3914 
3915             if (eventsByName.ContainsKey(evtName))
3916             {
3917                 manifest.ManifestError(SR.Format(SR.EventSource_EventNameReused, evtName), true);
3918             }
3919 
3920             eventsByName[evtName] = evtName;
3921         }
3922 
3923         /// <summary>
3924         /// This method looks at the IL and tries to pattern match against the standard
3925         /// 'boilerplate' event body
3926         /// <code>
3927         ///     { if (Enabled()) WriteEvent(#, ...) }
3928         /// </code>
3929         /// If the pattern matches, it returns the literal number passed as the first parameter to
3930         /// the WriteEvent.  This is used to find common user errors (mismatching this
3931         /// number with the EventAttribute ID).  It is only used for validation.
3932         /// </summary>
3933         /// <param name="method">The method to probe.</param>
3934         /// <returns>The literal value or -1 if the value could not be determined. </returns>
3935         [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Maintainability", "CA1502:AvoidExcessiveComplexity", Justification = "Switch statement is clearer than alternatives")]
GetHelperCallFirstArg(MethodInfo method)3936         static private int GetHelperCallFirstArg(MethodInfo method)
3937         {
3938 #if (!ES_BUILD_PCL && !ES_BUILD_PN)
3939             // Currently searches for the following pattern
3940             //
3941             // ...     // CAN ONLY BE THE INSTRUCTIONS BELOW
3942             // LDARG0
3943             // LDC.I4 XXX
3944             // ...     // CAN ONLY BE THE INSTRUCTIONS BELOW CAN'T BE A BRANCH OR A CALL
3945             // CALL
3946             // NOP     // 0 or more times
3947             // RET
3948             //
3949             // If we find this pattern we return the XXX.  Otherwise we return -1.
3950 #if !CORECLR
3951             (new ReflectionPermission(ReflectionPermissionFlag.MemberAccess)).Assert();
3952 #endif
3953             byte[] instrs = method.GetMethodBody().GetILAsByteArray();
3954             int retVal = -1;
3955             for (int idx = 0; idx < instrs.Length;)
3956             {
3957                 switch (instrs[idx])
3958                 {
3959                     case 0: // NOP
3960                     case 1: // BREAK
3961                     case 2: // LDARG_0
3962                     case 3: // LDARG_1
3963                     case 4: // LDARG_2
3964                     case 5: // LDARG_3
3965                     case 6: // LDLOC_0
3966                     case 7: // LDLOC_1
3967                     case 8: // LDLOC_2
3968                     case 9: // LDLOC_3
3969                     case 10: // STLOC_0
3970                     case 11: // STLOC_1
3971                     case 12: // STLOC_2
3972                     case 13: // STLOC_3
3973                         break;
3974                     case 14: // LDARG_S
3975                     case 16: // STARG_S
3976                         idx++;
3977                         break;
3978                     case 20: // LDNULL
3979                         break;
3980                     case 21: // LDC_I4_M1
3981                     case 22: // LDC_I4_0
3982                     case 23: // LDC_I4_1
3983                     case 24: // LDC_I4_2
3984                     case 25: // LDC_I4_3
3985                     case 26: // LDC_I4_4
3986                     case 27: // LDC_I4_5
3987                     case 28: // LDC_I4_6
3988                     case 29: // LDC_I4_7
3989                     case 30: // LDC_I4_8
3990                         if (idx > 0 && instrs[idx - 1] == 2)  // preceeded by LDARG0
3991                             retVal = instrs[idx] - 22;
3992                         break;
3993                     case 31: // LDC_I4_S
3994                         if (idx > 0 && instrs[idx - 1] == 2)  // preceeded by LDARG0
3995                             retVal = instrs[idx + 1];
3996                         idx++;
3997                         break;
3998                     case 32: // LDC_I4
3999                         idx += 4;
4000                         break;
4001                     case 37: // DUP
4002                         break;
4003                     case 40: // CALL
4004                         idx += 4;
4005 
4006                         if (retVal >= 0)
4007                         {
4008                             // Is this call just before return?
4009                             for (int search = idx + 1; search < instrs.Length; search++)
4010                             {
4011                                 if (instrs[search] == 42)  // RET
4012                                     return retVal;
4013                                 if (instrs[search] != 0)   // NOP
4014                                     break;
4015                             }
4016                         }
4017                         retVal = -1;
4018                         break;
4019                     case 44: // BRFALSE_S
4020                     case 45: // BRTRUE_S
4021                         retVal = -1;
4022                         idx++;
4023                         break;
4024                     case 57: // BRFALSE
4025                     case 58: // BRTRUE
4026                         retVal = -1;
4027                         idx += 4;
4028                         break;
4029                     case 103: // CONV_I1
4030                     case 104: // CONV_I2
4031                     case 105: // CONV_I4
4032                     case 106: // CONV_I8
4033                     case 109: // CONV_U4
4034                     case 110: // CONV_U8
4035                         break;
4036                     case 140: // BOX
4037                     case 141: // NEWARR
4038                         idx += 4;
4039                         break;
4040                     case 162: // STELEM_REF
4041                         break;
4042                     case 254: // PREFIX
4043                         idx++;
4044                         // Covers the CEQ instructions used in debug code for some reason.
4045                         if (idx >= instrs.Length || instrs[idx] >= 6)
4046                             goto default;
4047                         break;
4048                     default:
4049                         /* Debug.Fail("Warning: User validation code sub-optimial: Unsuported opcode " + instrs[idx] +
4050                             " at " + idx + " in method " + method.Name); */
4051                         return -1;
4052                 }
4053                 idx++;
4054             }
4055 #endif
4056             return -1;
4057         }
4058 
4059 #if false // This routine is not needed at all, it was used for unit test debugging.
4060         [Conditional("DEBUG")]
OutputDebugString(string msg)4061         private static void OutputDebugString(string msg)
4062         {
4063 #if !ES_BUILD_PCL
4064             msg = msg.TrimEnd('\r', '\n') +
4065                     string.Format(CultureInfo.InvariantCulture, ", Thrd({0})" + Environment.NewLine, Thread.CurrentThread.ManagedThreadId);
4066             System.Diagnostics.Debugger.Log(0, null, msg);
4067 #endif
4068         }
4069 #endif
4070 
4071         /// <summary>
4072         /// Sends an error message to the debugger (outputDebugString), as well as the EventListeners
4073         /// It will do this even if the EventSource is not enabled.
4074         /// TODO remove flush parameter it is not used.
4075         /// </summary>
4076         [SuppressMessage("Microsoft.Concurrency", "CA8001", Justification = "This does not need to be correct when racing with other threads")]
ReportOutOfBandMessage(string msg, bool flush)4077         internal void ReportOutOfBandMessage(string msg, bool flush)
4078         {
4079             try
4080             {
4081 #if (!ES_BUILD_PCL && !ES_BUILD_PN)
4082                 // send message to debugger without delay
4083                 System.Diagnostics.Debugger.Log(0, null, String.Format("EventSource Error: {0}{1}", msg, Environment.NewLine));
4084 #endif
4085 
4086                 // Send it to all listeners.
4087                 if (m_outOfBandMessageCount < 16 - 1)     // Note this is only if size byte
4088                     m_outOfBandMessageCount++;
4089                 else
4090                 {
4091                     if (m_outOfBandMessageCount == 16)
4092                         return;
4093                     m_outOfBandMessageCount = 16;    // Mark that we hit the limit.  Notify them that this is the case.
4094                     msg = "Reached message limit.   End of EventSource error messages.";
4095                 }
4096 
4097                 WriteEventString(EventLevel.LogAlways, -1, msg);
4098                 WriteStringToAllListeners("EventSourceMessage", msg);
4099             }
4100             catch (Exception) { }      // If we fail during last chance logging, well, we have to give up....
4101         }
4102 
ValidateSettings(EventSourceSettings settings)4103         private EventSourceSettings ValidateSettings(EventSourceSettings settings)
4104         {
4105             var evtFormatMask = EventSourceSettings.EtwManifestEventFormat |
4106                                 EventSourceSettings.EtwSelfDescribingEventFormat;
4107             if ((settings & evtFormatMask) == evtFormatMask)
4108             {
4109                 throw new ArgumentException(SR.EventSource_InvalidEventFormat, nameof(settings));
4110             }
4111 
4112             // If you did not explicitly ask for manifest, you get self-describing.
4113             if ((settings & evtFormatMask) == 0)
4114                 settings |= EventSourceSettings.EtwSelfDescribingEventFormat;
4115             return settings;
4116         }
4117 
4118         private bool ThrowOnEventWriteErrors
4119         {
4120             get { return (m_config & EventSourceSettings.ThrowOnEventWriteErrors) != 0; }
4121             set
4122             {
4123                 if (value) m_config |= EventSourceSettings.ThrowOnEventWriteErrors;
4124                 else m_config &= ~EventSourceSettings.ThrowOnEventWriteErrors;
4125             }
4126         }
4127 
4128         private bool SelfDescribingEvents
4129         {
4130             get
4131             {
4132                 Debug.Assert(((m_config & EventSourceSettings.EtwManifestEventFormat) != 0) !=
4133                                 ((m_config & EventSourceSettings.EtwSelfDescribingEventFormat) != 0));
4134                 return (m_config & EventSourceSettings.EtwSelfDescribingEventFormat) != 0;
4135             }
4136             set
4137             {
4138                 if (!value)
4139                 {
4140                     m_config |= EventSourceSettings.EtwManifestEventFormat;
4141                     m_config &= ~EventSourceSettings.EtwSelfDescribingEventFormat;
4142                 }
4143                 else
4144                 {
4145                     m_config |= EventSourceSettings.EtwSelfDescribingEventFormat;
4146                     m_config &= ~EventSourceSettings.EtwManifestEventFormat;
4147                 }
4148             }
4149         }
4150 
4151 #if FEATURE_ACTIVITYSAMPLING
ReportActivitySamplingInfo(EventListener listener, SessionMask sessions)4152         private void ReportActivitySamplingInfo(EventListener listener, SessionMask sessions)
4153         {
4154             Debug.Assert(listener == null || (uint)sessions == (uint)SessionMask.FromId(0));
4155 
4156             for (int perEventSourceSessionId = 0; perEventSourceSessionId < SessionMask.MAX; ++perEventSourceSessionId)
4157             {
4158                 if (!sessions[perEventSourceSessionId])
4159                     continue;
4160 
4161                 ActivityFilter af;
4162                 if (listener == null)
4163                 {
4164                     EtwSession etwSession = m_etwSessionIdMap[perEventSourceSessionId];
4165                     Debug.Assert(etwSession != null);
4166                     af = etwSession.m_activityFilter;
4167                 }
4168                 else
4169                 {
4170                     af = listener.m_activityFilter;
4171                 }
4172 
4173                 if (af == null)
4174                     continue;
4175 
4176                 SessionMask m = new SessionMask();
4177                 m[perEventSourceSessionId] = true;
4178 
4179                 foreach (var t in af.GetFilterAsTuple(m_guid))
4180                 {
4181                     WriteStringToListener(listener, string.Format(CultureInfo.InvariantCulture, "Session {0}: {1} = {2}", perEventSourceSessionId, t.Item1, t.Item2), m);
4182                 }
4183 
4184                 bool participateInSampling = (listener == null) ?
4185                                                m_activityFilteringForETWEnabled[perEventSourceSessionId] :
4186                                                GetDispatcher(listener).m_activityFilteringEnabled;
4187                 WriteStringToListener(listener, string.Format(CultureInfo.InvariantCulture, "Session {0}: Activity Sampling support: {1}",
4188                                           perEventSourceSessionId, participateInSampling ? "enabled" : "disabled"), m);
4189             }
4190         }
4191 #endif // FEATURE_ACTIVITYSAMPLING
4192 
4193         // private instance state
4194         private string m_name;                          // My friendly name (privided in ctor)
4195         internal int m_id;                              // A small integer that is unique to this instance.
4196         private Guid m_guid;                            // GUID representing the ETW eventSource to the OS.
4197         internal volatile EventMetadata[] m_eventData;  // None per-event data
4198         private volatile byte[] m_rawManifest;          // Bytes to send out representing the event schema
4199 
4200         private EventHandler<EventCommandEventArgs> m_eventCommandExecuted;
4201 
4202         private EventSourceSettings m_config;      // configuration information
4203 
4204         private bool m_eventSourceDisposed;              // has Dispose been called.
4205 
4206         // Enabling bits
4207         private bool m_eventSourceEnabled;              // am I enabled (any of my events are enabled for any dispatcher)
4208         internal EventLevel m_level;                    // highest level enabled by any output dispatcher
4209         internal EventKeywords m_matchAnyKeyword;       // the logical OR of all levels enabled by any output dispatcher (zero is a special case) meaning 'all keywords'
4210 
4211         // Dispatching state
4212         internal volatile EventDispatcher m_Dispatchers;    // Linked list of code:EventDispatchers we write the data to (we also do ETW specially)
4213 #if FEATURE_MANAGED_ETW
4214         private volatile OverideEventProvider m_provider;   // This hooks up ETW commands to our 'OnEventCommand' callback
4215 #endif
4216         private bool m_completelyInited;                // The EventSource constructor has returned without exception.
4217         private Exception m_constructionException;      // If there was an exception construction, this is it
4218         private byte m_outOfBandMessageCount;           // The number of out of band messages sent (we throttle them
4219         private EventCommandEventArgs m_deferredCommands;// If we get commands before we are fully we store them here and run the when we are fully inited.
4220 
4221         private string[] m_traits;                      // Used to implement GetTraits
4222 
4223         internal static uint s_currentPid;              // current process id, used in synthesizing quasi-GUIDs
4224         [ThreadStatic]
4225         private static byte m_EventSourceExceptionRecurenceCount = 0; // current recursion count inside ThrowEventSourceException
4226 
4227         [ThreadStatic]
4228         private static bool m_EventSourceInDecodeObject = false;
4229 
4230 #if FEATURE_MANAGED_ETW_CHANNELS
4231         internal volatile ulong[] m_channelData;
4232 #endif
4233 
4234 #if FEATURE_ACTIVITYSAMPLING
4235         private SessionMask m_curLiveSessions;          // the activity-tracing aware sessions' bits
4236         private EtwSession[] m_etwSessionIdMap;         // the activity-tracing aware sessions
4237         private List<EtwSession> m_legacySessions;      // the legacy ETW sessions listening to this source
4238         internal long m_keywordTriggers;                // a bit is set if it corresponds to a keyword that's part of an enabled triggering event
4239         internal SessionMask m_activityFilteringForETWEnabled; // does THIS EventSource have activity filtering turned on for each ETW session
4240         static internal Action<Guid> s_activityDying;   // Fires when something calls SetCurrentThreadToActivity()
4241         // Also used to mark that activity tracing is on for some case
4242 #endif // FEATURE_ACTIVITYSAMPLING
4243 
4244         // We use a single instance of ActivityTracker for all EventSources instances to allow correlation between multiple event providers.
4245         // We have m_activityTracker field simply because instance field is more efficient than static field fetch.
4246         ActivityTracker m_activityTracker;
4247         internal const string s_ActivityStartSuffix = "Start";
4248         internal const string s_ActivityStopSuffix = "Stop";
4249 
4250         // used for generating GUID from eventsource name
4251         private static readonly byte[] namespaceBytes = new byte[] {
4252             0x48, 0x2C, 0x2D, 0xB2, 0xC3, 0x90, 0x47, 0xC8,
4253             0x87, 0xF8, 0x1A, 0x15, 0xBF, 0xC1, 0x30, 0xFB,
4254         };
4255 
4256 #endregion
4257     }
4258 
4259     /// <summary>
4260     /// Enables specifying event source configuration options to be used in the EventSource constructor.
4261     /// </summary>
4262     [Flags]
4263     public enum EventSourceSettings
4264     {
4265         /// <summary>
4266         /// This specifies none of the special configuration options should be enabled.
4267         /// </summary>
4268         Default = 0,
4269         /// <summary>
4270         /// Normally an EventSource NEVER throws; setting this option will tell it to throw when it encounters errors.
4271         /// </summary>
4272         ThrowOnEventWriteErrors = 1,
4273         /// <summary>
4274         /// Setting this option is a directive to the ETW listener should use manifest-based format when
4275         /// firing events. This is the default option when defining a type derived from EventSource
4276         /// (using the protected EventSource constructors).
4277         /// Only one of EtwManifestEventFormat or EtwSelfDescribingEventFormat should be specified
4278         /// </summary>
4279         EtwManifestEventFormat = 4,
4280         /// <summary>
4281         /// Setting this option is a directive to the ETW listener should use self-describing event format
4282         /// when firing events. This is the default option when creating a new instance of the EventSource
4283         /// type (using the public EventSource constructors).
4284         /// Only one of EtwManifestEventFormat or EtwSelfDescribingEventFormat should be specified
4285         /// </summary>
4286         EtwSelfDescribingEventFormat = 8,
4287     }
4288 
4289     /// <summary>
4290     /// An EventListener represents a target for the events generated by EventSources (that is subclasses
4291     /// of <see cref="EventSource"/>), in the current appdomain. When a new EventListener is created
4292     /// it is logically attached to all eventSources in that appdomain. When the EventListener is Disposed, then
4293     /// it is disconnected from the event eventSources. Note that there is a internal list of STRONG references
4294     /// to EventListeners, which means that relying on the lack of references to EventListeners to clean up
4295     /// EventListeners will NOT work. You must call EventListener.Dispose explicitly when a dispatcher is no
4296     /// longer needed.
4297     /// <para>
4298     /// Once created, EventListeners can enable or disable on a per-eventSource basis using verbosity levels
4299     /// (<see cref="EventLevel"/>) and bitfields (<see cref="EventKeywords"/>) to further restrict the set of
4300     /// events to be sent to the dispatcher. The dispatcher can also send arbitrary commands to a particular
4301     /// eventSource using the 'SendCommand' method. The meaning of the commands are eventSource specific.
4302     /// </para><para>
4303     /// The Null Guid (that is (new Guid()) has special meaning as a wildcard for 'all current eventSources in
4304     /// the appdomain'. Thus it is relatively easy to turn on all events in the appdomain if desired.
4305     /// </para><para>
4306     /// It is possible for there to be many EventListener's defined in a single appdomain. Each dispatcher is
4307     /// logically independent of the other listeners. Thus when one dispatcher enables or disables events, it
4308     /// affects only that dispatcher (other listeners get the events they asked for). It is possible that
4309     /// commands sent with 'SendCommand' would do a semantic operation that would affect the other listeners
4310     /// (like doing a GC, or flushing data ...), but this is the exception rather than the rule.
4311     /// </para><para>
4312     /// Thus the model is that each EventSource keeps a list of EventListeners that it is sending events
4313     /// to. Associated with each EventSource-dispatcher pair is a set of filtering criteria that determine for
4314     /// that eventSource what events that dispatcher will receive.
4315     /// </para><para>
4316     /// Listeners receive the events on their 'OnEventWritten' method. Thus subclasses of EventListener must
4317     /// override this method to do something useful with the data.
4318     /// </para><para>
4319     /// In addition, when new eventSources are created, the 'OnEventSourceCreate' method is called. The
4320     /// invariant associated with this callback is that every eventSource gets exactly one
4321     /// 'OnEventSourceCreate' call for ever eventSource that can potentially send it log messages. In
4322     /// particular when a EventListener is created, typically a series of OnEventSourceCreate' calls are
4323     /// made to notify the new dispatcher of all the eventSources that existed before the EventListener was
4324     /// created.
4325     /// </para>
4326     /// </summary>
4327     public class EventListener : IDisposable
4328     {
4329         private event EventHandler<EventSourceCreatedEventArgs> _EventSourceCreated;
4330 
4331         /// <summary>
4332         /// This event is raised whenever a new eventSource is 'attached' to the dispatcher.
4333         /// This can happen for all existing EventSources when the EventListener is created
4334         /// as well as for any EventSources that come into existence after the EventListener
4335         /// has been created.
4336         ///
4337         /// These 'catch up' events are called during the construction of the EventListener.
4338         /// Subclasses need to be prepared for that.
4339         ///
4340         /// In a multi-threaded environment, it is possible that 'EventSourceEventWrittenCallback'
4341         /// events for a particular eventSource to occur BEFORE the EventSourceCreatedCallback is issued.
4342         /// </summary>
4343         public event EventHandler<EventSourceCreatedEventArgs> EventSourceCreated
4344         {
4345             add
4346             {
4347                 CallBackForExistingEventSources(false, value);
4348 
4349                 this._EventSourceCreated = (EventHandler<EventSourceCreatedEventArgs>)Delegate.Combine(_EventSourceCreated, value);
4350             }
4351             remove
4352             {
4353                 this._EventSourceCreated = (EventHandler<EventSourceCreatedEventArgs>)Delegate.Remove(_EventSourceCreated, value);
4354             }
4355         }
4356 
4357         /// <summary>
4358         /// This event is raised whenever an event has been written by a EventSource for which
4359         /// the EventListener has enabled events.
4360         /// </summary>
4361         public event EventHandler<EventWrittenEventArgs> EventWritten;
4362 
4363         /// <summary>
4364         /// Create a new EventListener in which all events start off turned off (use EnableEvents to turn
4365         /// them on).
4366         /// </summary>
EventListener()4367         public EventListener()
4368         {
4369             // This will cause the OnEventSourceCreated callback to fire.
4370             CallBackForExistingEventSources(true, (obj, args) => args.EventSource.AddListener((EventListener)obj));
4371         }
4372 
4373         /// <summary>
4374         /// Dispose should be called when the EventListener no longer desires 'OnEvent*' callbacks. Because
4375         /// there is an internal list of strong references to all EventListeners, calling 'Dispose' directly
4376         /// is the only way to actually make the listen die. Thus it is important that users of EventListener
4377         /// call Dispose when they are done with their logging.
4378         /// </summary>
4379 #if ES_BUILD_STANDALONE
4380         [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1063:ImplementIDisposableCorrectly")]
4381 #endif
Dispose()4382         public virtual void Dispose()
4383         {
4384             lock (EventListenersLock)
4385             {
4386                 if (s_Listeners != null)
4387                 {
4388                     if (this == s_Listeners)
4389                     {
4390                         EventListener cur = s_Listeners;
4391                         s_Listeners = this.m_Next;
4392                         RemoveReferencesToListenerInEventSources(cur);
4393                     }
4394                     else
4395                     {
4396                         // Find 'this' from the s_Listeners linked list.
4397                         EventListener prev = s_Listeners;
4398                         for (;;)
4399                         {
4400                             EventListener cur = prev.m_Next;
4401                             if (cur == null)
4402                                 break;
4403                             if (cur == this)
4404                             {
4405                                 // Found our Listener, remove references to to it in the eventSources
4406                                 prev.m_Next = cur.m_Next;       // Remove entry.
4407                                 RemoveReferencesToListenerInEventSources(cur);
4408                                 break;
4409                             }
4410                             prev = cur;
4411                         }
4412                     }
4413                 }
4414                 Validate();
4415             }
4416         }
4417         // We don't expose a Dispose(bool), because the contract is that you don't have any non-syncronous
4418         // 'cleanup' associated with this object
4419 
4420         /// <summary>
4421         /// Enable all events from the eventSource identified by 'eventSource' to the current
4422         /// dispatcher that have a verbosity level of 'level' or lower.
4423         ///
4424         /// This call can have the effect of REDUCING the number of events sent to the
4425         /// dispatcher if 'level' indicates a less verbose level than was previously enabled.
4426         ///
4427         /// This call never has an effect on other EventListeners.
4428         ///
4429         /// </summary>
EnableEvents(EventSource eventSource, EventLevel level)4430         public void EnableEvents(EventSource eventSource, EventLevel level)
4431         {
4432             EnableEvents(eventSource, level, EventKeywords.None);
4433         }
4434         /// <summary>
4435         /// Enable all events from the eventSource identified by 'eventSource' to the current
4436         /// dispatcher that have a verbosity level of 'level' or lower and have a event keyword
4437         /// matching any of the bits in 'matchAnyKeyword'.
4438         ///
4439         /// This call can have the effect of REDUCING the number of events sent to the
4440         /// dispatcher if 'level' indicates a less verbose level than was previously enabled or
4441         /// if 'matchAnyKeyword' has fewer keywords set than where previously set.
4442         ///
4443         /// This call never has an effect on other EventListeners.
4444         /// </summary>
EnableEvents(EventSource eventSource, EventLevel level, EventKeywords matchAnyKeyword)4445         public void EnableEvents(EventSource eventSource, EventLevel level, EventKeywords matchAnyKeyword)
4446         {
4447             EnableEvents(eventSource, level, matchAnyKeyword, null);
4448         }
4449         /// <summary>
4450         /// Enable all events from the eventSource identified by 'eventSource' to the current
4451         /// dispatcher that have a verbosity level of 'level' or lower and have a event keyword
4452         /// matching any of the bits in 'matchAnyKeyword' as well as any (eventSource specific)
4453         /// effect passing additional 'key-value' arguments 'arguments' might have.
4454         ///
4455         /// This call can have the effect of REDUCING the number of events sent to the
4456         /// dispatcher if 'level' indicates a less verbose level than was previously enabled or
4457         /// if 'matchAnyKeyword' has fewer keywords set than where previously set.
4458         ///
4459         /// This call never has an effect on other EventListeners.
4460         /// </summary>
EnableEvents(EventSource eventSource, EventLevel level, EventKeywords matchAnyKeyword, IDictionary<string, string> arguments)4461         public void EnableEvents(EventSource eventSource, EventLevel level, EventKeywords matchAnyKeyword, IDictionary<string, string> arguments)
4462         {
4463             if (eventSource == null)
4464             {
4465                 throw new ArgumentNullException(nameof(eventSource));
4466             }
4467 
4468             eventSource.SendCommand(this, 0, 0, EventCommand.Update, true, level, matchAnyKeyword, arguments);
4469         }
4470         /// <summary>
4471         /// Disables all events coming from eventSource identified by 'eventSource'.
4472         ///
4473         /// This call never has an effect on other EventListeners.
4474         /// </summary>
DisableEvents(EventSource eventSource)4475         public void DisableEvents(EventSource eventSource)
4476         {
4477             if (eventSource == null)
4478             {
4479                 throw new ArgumentNullException(nameof(eventSource));
4480             }
4481 
4482             eventSource.SendCommand(this, 0, 0, EventCommand.Update, false, EventLevel.LogAlways, EventKeywords.None, null);
4483         }
4484 
4485         /// <summary>
4486         /// EventSourceIndex is small non-negative integer (suitable for indexing in an array)
4487         /// identifying EventSource. It is unique per-appdomain. Some EventListeners might find
4488         /// it useful to store additional information about each eventSource connected to it,
4489         /// and EventSourceIndex allows this extra information to be efficiently stored in a
4490         /// (growable) array (eg List(T)).
4491         /// </summary>
EventSourceIndex(EventSource eventSource)4492         public static int EventSourceIndex(EventSource eventSource) { return eventSource.m_id; }
4493 
4494         /// <summary>
4495         /// This method is called whenever a new eventSource is 'attached' to the dispatcher.
4496         /// This can happen for all existing EventSources when the EventListener is created
4497         /// as well as for any EventSources that come into existence after the EventListener
4498         /// has been created.
4499         ///
4500         /// These 'catch up' events are called during the construction of the EventListener.
4501         /// Subclasses need to be prepared for that.
4502         ///
4503         /// In a multi-threaded environment, it is possible that 'OnEventWritten' callbacks
4504         /// for a particular eventSource to occur BEFORE the OnEventSourceCreated is issued.
4505         /// </summary>
4506         /// <param name="eventSource"></param>
OnEventSourceCreated(EventSource eventSource)4507         internal protected virtual void OnEventSourceCreated(EventSource eventSource)
4508         {
4509             EventHandler<EventSourceCreatedEventArgs> callBack = this._EventSourceCreated;
4510             if (callBack != null)
4511             {
4512                 EventSourceCreatedEventArgs args = new EventSourceCreatedEventArgs();
4513                 args.EventSource = eventSource;
4514                 callBack(this, args);
4515             }
4516         }
4517 
4518         /// <summary>
4519         /// This method is called whenever an event has been written by a EventSource for which
4520         /// the EventListener has enabled events.
4521         /// </summary>
4522         /// <param name="eventData"></param>
OnEventWritten(EventWrittenEventArgs eventData)4523         internal protected virtual void OnEventWritten(EventWrittenEventArgs eventData)
4524         {
4525             EventHandler<EventWrittenEventArgs> callBack = this.EventWritten;
4526             if (callBack != null)
4527             {
4528                 callBack(this, eventData);
4529             }
4530         }
4531 
4532 
4533 #region private
4534         /// <summary>
4535         /// This routine adds newEventSource to the global list of eventSources, it also assigns the
4536         /// ID to the eventSource (which is simply the ordinal in the global list).
4537         ///
4538         /// EventSources currently do not pro-actively remove themselves from this list. Instead
4539         /// when eventSources's are GCed, the weak handle in this list naturally gets nulled, and
4540         /// we will reuse the slot. Today this list never shrinks (but we do reuse entries
4541         /// that are in the list). This seems OK since the expectation is that EventSources
4542         /// tend to live for the lifetime of the appdomain anyway (they tend to be used in
4543         /// global variables).
4544         /// </summary>
4545         /// <param name="newEventSource"></param>
AddEventSource(EventSource newEventSource)4546         internal static void AddEventSource(EventSource newEventSource)
4547         {
4548             lock (EventListenersLock)
4549             {
4550                 if (s_EventSources == null)
4551                     s_EventSources = new List<WeakReference>(2);
4552 
4553                 if (!s_EventSourceShutdownRegistered)
4554                 {
4555                     s_EventSourceShutdownRegistered = true;
4556                 }
4557 
4558 
4559                 // Periodically search the list for existing entries to reuse, this avoids
4560                 // unbounded memory use if we keep recycling eventSources (an unlikely thing).
4561                 int newIndex = -1;
4562                 if (s_EventSources.Count % 64 == 63)   // on every block of 64, fill up the block before continuing
4563                 {
4564                     int i = s_EventSources.Count;      // Work from the top down.
4565                     while (0 < i)
4566                     {
4567                         --i;
4568                         WeakReference weakRef = s_EventSources[i];
4569                         if (!weakRef.IsAlive)
4570                         {
4571                             newIndex = i;
4572                             weakRef.Target = newEventSource;
4573                             break;
4574                         }
4575                     }
4576                 }
4577                 if (newIndex < 0)
4578                 {
4579                     newIndex = s_EventSources.Count;
4580                     s_EventSources.Add(new WeakReference(newEventSource));
4581                 }
4582                 newEventSource.m_id = newIndex;
4583 
4584                 // Add every existing dispatcher to the new EventSource
4585                 for (EventListener listener = s_Listeners; listener != null; listener = listener.m_Next)
4586                     newEventSource.AddListener(listener);
4587 
4588                 Validate();
4589             }
4590         }
4591 
4592         // Whenver we have async callbacks from native code, there is an ugly issue where
4593         // during .NET shutdown native code could be calling the callback, but the CLR
4594         // has already prohibited callbacks to managed code in the appdomain, causing the CLR
4595         // to throw a COMPLUS_BOOT_EXCEPTION.   The guideline we give is that you must unregister
4596         // such callbacks on process shutdown or appdomain so that unmanaged code will never
4597         // do this.  This is what this callback is for.
4598         // See bug 724140 for more
DisposeOnShutdown(object sender, EventArgs e)4599         private static void DisposeOnShutdown(object sender, EventArgs e)
4600         {
4601             lock (EventListenersLock)
4602             {
4603                 foreach (var esRef in s_EventSources)
4604                 {
4605                     EventSource es = esRef.Target as EventSource;
4606                     if (es != null)
4607                         es.Dispose();
4608                 }
4609             }
4610         }
4611 
4612         /// <summary>
4613         /// Helper used in code:Dispose that removes any references to 'listenerToRemove' in any of the
4614         /// eventSources in the appdomain.
4615         ///
4616         /// The EventListenersLock must be held before calling this routine.
4617         /// </summary>
RemoveReferencesToListenerInEventSources(EventListener listenerToRemove)4618         private static void RemoveReferencesToListenerInEventSources(EventListener listenerToRemove)
4619         {
4620 #if !ES_BUILD_STANDALONE
4621             Debug.Assert(Monitor.IsEntered(EventListener.EventListenersLock));
4622 #endif
4623             // Foreach existing EventSource in the appdomain
4624             foreach (WeakReference eventSourceRef in s_EventSources)
4625             {
4626                 EventSource eventSource = eventSourceRef.Target as EventSource;
4627                 if (eventSource != null)
4628                 {
4629                     // Is the first output dispatcher the dispatcher we are removing?
4630                     if (eventSource.m_Dispatchers.m_Listener == listenerToRemove)
4631                         eventSource.m_Dispatchers = eventSource.m_Dispatchers.m_Next;
4632                     else
4633                     {
4634                         // Remove 'listenerToRemove' from the eventSource.m_Dispatchers linked list.
4635                         EventDispatcher prev = eventSource.m_Dispatchers;
4636                         for (;;)
4637                         {
4638                             EventDispatcher cur = prev.m_Next;
4639                             if (cur == null)
4640                             {
4641                                 Debug.Fail("EventSource did not have a registered EventListener!");
4642                                 break;
4643                             }
4644                             if (cur.m_Listener == listenerToRemove)
4645                             {
4646                                 prev.m_Next = cur.m_Next;       // Remove entry.
4647                                 break;
4648                             }
4649                             prev = cur;
4650                         }
4651                     }
4652                 }
4653             }
4654         }
4655 
4656         /// <summary>
4657         /// Checks internal consistency of EventSources/Listeners.
4658         /// </summary>
4659         [Conditional("DEBUG")]
Validate()4660         internal static void Validate()
4661         {
4662             lock (EventListenersLock)
4663             {
4664                 // Get all listeners
4665                 Dictionary<EventListener, bool> allListeners = new Dictionary<EventListener, bool>();
4666                 EventListener cur = s_Listeners;
4667                 while (cur != null)
4668                 {
4669                     allListeners.Add(cur, true);
4670                     cur = cur.m_Next;
4671                 }
4672 
4673                 // For all eventSources
4674                 int id = -1;
4675                 foreach (WeakReference eventSourceRef in s_EventSources)
4676                 {
4677                     id++;
4678                     EventSource eventSource = eventSourceRef.Target as EventSource;
4679                     if (eventSource == null)
4680                         continue;
4681                     Debug.Assert(eventSource.m_id == id, "Unexpected event source ID.");
4682 
4683                     // None listeners on eventSources exist in the dispatcher list.
4684                     EventDispatcher dispatcher = eventSource.m_Dispatchers;
4685                     while (dispatcher != null)
4686                     {
4687                         Debug.Assert(allListeners.ContainsKey(dispatcher.m_Listener), "EventSource has a listener not on the global list.");
4688                         dispatcher = dispatcher.m_Next;
4689                     }
4690 
4691                     // Every dispatcher is on Dispatcher List of every eventSource.
4692                     foreach (EventListener listener in allListeners.Keys)
4693                     {
4694                         dispatcher = eventSource.m_Dispatchers;
4695                         for (;;)
4696                         {
4697                             Debug.Assert(dispatcher != null, "Listener is not on all eventSources.");
4698                             if (dispatcher.m_Listener == listener)
4699                                 break;
4700                             dispatcher = dispatcher.m_Next;
4701                         }
4702                     }
4703                 }
4704             }
4705         }
4706 
4707         /// <summary>
4708         /// Gets a global lock that is intended to protect the code:s_Listeners linked list and the
4709         /// code:s_EventSources WeakReference list.  (We happen to use the s_EventSources list as
4710         /// the lock object)
4711         /// </summary>
4712         internal static object EventListenersLock
4713         {
4714             get
4715             {
4716                 if (s_EventSources == null)
4717                     Interlocked.CompareExchange(ref s_EventSources, new List<WeakReference>(2), null);
4718                 return s_EventSources;
4719             }
4720         }
4721 
CallBackForExistingEventSources(bool addToListenersList, EventHandler<EventSourceCreatedEventArgs> callback)4722         private void CallBackForExistingEventSources(bool addToListenersList, EventHandler<EventSourceCreatedEventArgs> callback)
4723         {
4724             lock (EventListenersLock)
4725             {
4726                 // Disallow creating EventListener reentrancy.
4727                 if (s_CreatingListener)
4728                 {
4729                     throw new InvalidOperationException(SR.EventSource_ListenerCreatedInsideCallback);
4730                 }
4731 
4732                 try
4733                 {
4734                     s_CreatingListener = true;
4735 
4736                     if (addToListenersList)
4737                     {
4738                         // Add to list of listeners in the system, do this BEFORE firing the 'OnEventSourceCreated' so that
4739                         // Those added sources see this listener.
4740                         this.m_Next = s_Listeners;
4741                         s_Listeners = this;
4742                     }
4743 
4744                     // Find all existing eventSources call OnEventSourceCreated to 'catchup'
4745                     // Note that we DO have reentrancy here because 'AddListener' calls out to user code (via OnEventSourceCreated callback)
4746                     // We tolerate this by iterating over a copy of the list here. New event sources will take care of adding listeners themselves
4747                     // EventSources are not guaranteed to be added at the end of the s_EventSource list -- We re-use slots when a new source
4748                     // is created.
4749                     WeakReference[] eventSourcesSnapshot = s_EventSources.ToArray();
4750 
4751                     for (int i = 0; i < eventSourcesSnapshot.Length; i++)
4752                     {
4753                         WeakReference eventSourceRef = eventSourcesSnapshot[i];
4754                         EventSource eventSource = eventSourceRef.Target as EventSource;
4755                         if (eventSource != null)
4756                         {
4757                             EventSourceCreatedEventArgs args = new EventSourceCreatedEventArgs();
4758                             args.EventSource = eventSource;
4759                             callback(this, args);
4760                         }
4761                     }
4762 
4763                     Validate();
4764                 }
4765                 finally
4766                 {
4767                     s_CreatingListener = false;
4768                 }
4769             }
4770 
4771         }
4772 
4773         // Instance fields
4774         internal volatile EventListener m_Next;                         // These form a linked list in s_Listeners
4775 #if FEATURE_ACTIVITYSAMPLING
4776         internal ActivityFilter m_activityFilter;                       // If we are filtering by activity on this Listener, this keeps track of it.
4777 #endif // FEATURE_ACTIVITYSAMPLING
4778 
4779         // static fields
4780 
4781         /// <summary>
4782         /// The list of all listeners in the appdomain.  Listeners must be explicitly disposed to remove themselves
4783         /// from this list.   Note that EventSources point to their listener but NOT the reverse.
4784         /// </summary>
4785         internal static EventListener s_Listeners;
4786         /// <summary>
4787         /// The list of all active eventSources in the appdomain.  Note that eventSources do NOT
4788         /// remove themselves from this list this is a weak list and the GC that removes them may
4789         /// not have happened yet.  Thus it can contain event sources that are dead (thus you have
4790         /// to filter those out.
4791         /// </summary>
4792         internal static List<WeakReference> s_EventSources;
4793 
4794         /// <summary>
4795         /// Used to disallow reentrancy.
4796         /// </summary>
4797         private static bool s_CreatingListener = false;
4798 
4799         /// <summary>
4800         /// Used to register AD/Process shutdown callbacks.
4801         /// </summary>
4802         private static bool s_EventSourceShutdownRegistered = false;
4803 #endregion
4804     }
4805 
4806     /// <summary>
4807     /// Passed to the code:EventSource.OnEventCommand callback
4808     /// </summary>
4809     public class EventCommandEventArgs : EventArgs
4810     {
4811         /// <summary>
4812         /// Gets the command for the callback.
4813         /// </summary>
4814         public EventCommand Command { get; internal set; }
4815 
4816         /// <summary>
4817         /// Gets the arguments for the callback.
4818         /// </summary>
4819         public IDictionary<String, String> Arguments { get; internal set; }
4820 
4821         /// <summary>
4822         /// Enables the event that has the specified identifier.
4823         /// </summary>
4824         /// <param name="eventId">Event ID of event to be enabled</param>
4825         /// <returns>true if eventId is in range</returns>
EnableEvent(int eventId)4826         public bool EnableEvent(int eventId)
4827         {
4828             if (Command != EventCommand.Enable && Command != EventCommand.Disable)
4829                 throw new InvalidOperationException();
4830             return eventSource.EnableEventForDispatcher(dispatcher, eventId, true);
4831         }
4832 
4833         /// <summary>
4834         /// Disables the event that have the specified identifier.
4835         /// </summary>
4836         /// <param name="eventId">Event ID of event to be disabled</param>
4837         /// <returns>true if eventId is in range</returns>
DisableEvent(int eventId)4838         public bool DisableEvent(int eventId)
4839         {
4840             if (Command != EventCommand.Enable && Command != EventCommand.Disable)
4841                 throw new InvalidOperationException();
4842             return eventSource.EnableEventForDispatcher(dispatcher, eventId, false);
4843         }
4844 
4845 #region private
4846 
EventCommandEventArgs(EventCommand command, IDictionary<string, string> arguments, EventSource eventSource, EventListener listener, int perEventSourceSessionId, int etwSessionId, bool enable, EventLevel level, EventKeywords matchAnyKeyword)4847         internal EventCommandEventArgs(EventCommand command, IDictionary<string, string> arguments, EventSource eventSource,
4848             EventListener listener, int perEventSourceSessionId, int etwSessionId, bool enable, EventLevel level, EventKeywords matchAnyKeyword)
4849         {
4850             this.Command = command;
4851             this.Arguments = arguments;
4852             this.eventSource = eventSource;
4853             this.listener = listener;
4854             this.perEventSourceSessionId = perEventSourceSessionId;
4855             this.etwSessionId = etwSessionId;
4856             this.enable = enable;
4857             this.level = level;
4858             this.matchAnyKeyword = matchAnyKeyword;
4859         }
4860 
4861         internal EventSource eventSource;
4862         internal EventDispatcher dispatcher;
4863 
4864         // These are the arguments of sendCommand and are only used for deferring commands until after we are fully initialized.
4865         internal EventListener listener;
4866         internal int perEventSourceSessionId;
4867         internal int etwSessionId;
4868         internal bool enable;
4869         internal EventLevel level;
4870         internal EventKeywords matchAnyKeyword;
4871         internal EventCommandEventArgs nextCommand;     // We form a linked list of these deferred commands.
4872 
4873 #endregion
4874     }
4875 
4876     /// <summary>
4877     /// EventSourceCreatedEventArgs is passed to <see cref="EventListener.EventSourceCreated"/>
4878     /// </summary>
4879     public class EventSourceCreatedEventArgs : EventArgs
4880     {
4881         /// <summary>
4882         /// The EventSource that is attaching to the listener.
4883         /// </summary>
4884         public EventSource EventSource
4885         {
4886             get;
4887             internal set;
4888         }
4889     }
4890 
4891     /// <summary>
4892     /// EventWrittenEventArgs is passed to the user-provided override for
4893     /// <see cref="EventListener.OnEventWritten"/> when an event is fired.
4894     /// </summary>
4895     public class EventWrittenEventArgs : EventArgs
4896     {
4897         /// <summary>
4898         /// The name of the event.
4899         /// </summary>
4900         public string EventName
4901         {
4902             get
4903             {
4904                 if (m_eventName != null || EventId < 0)      // TraceLogging convention EventID == -1
4905                 {
4906                     return m_eventName;
4907                 }
4908                 else
4909                     return m_eventSource.m_eventData[EventId].Name;
4910             }
4911             internal set
4912             {
4913                 m_eventName = value;
4914             }
4915         }
4916 
4917         /// <summary>
4918         /// Gets the event ID for the event that was written.
4919         /// </summary>
4920         public int EventId { get; internal set; }
4921 
4922         /// <summary>
4923         /// Gets the activity ID for the thread on which the event was written.
4924         /// </summary>
4925         public Guid ActivityId
4926         {
4927             get { return EventSource.CurrentThreadActivityId; }
4928         }
4929 
4930         /// <summary>
4931         /// Gets the related activity ID if one was specified when the event was written.
4932         /// </summary>
4933         public Guid RelatedActivityId
4934         {
4935             get;
4936             internal set;
4937         }
4938 
4939         /// <summary>
4940         /// Gets the payload for the event.
4941         /// </summary>
4942         public ReadOnlyCollection<Object> Payload { get; internal set; }
4943 
4944         /// <summary>
4945         /// Gets the payload argument names.
4946         /// </summary>
4947         public ReadOnlyCollection<string> PayloadNames
4948         {
4949             get
4950             {
4951                 // For contract based events we create the list lazily.
4952                 // You can have m_payloadNames be null in the TraceLogging case (EventID < 0) so only
4953                 // do the lazy init if you know it is contract based (EventID >= 0)
4954                 if (EventId >= 0 && m_payloadNames == null)
4955                 {
4956 
4957                     var names = new List<string>();
4958                     foreach (var parameter in m_eventSource.m_eventData[EventId].Parameters)
4959                     {
4960                         names.Add(parameter.Name);
4961                     }
4962                     m_payloadNames = new ReadOnlyCollection<string>(names);
4963                 }
4964 
4965                 return m_payloadNames;
4966             }
4967 
4968             internal set
4969             {
4970                 m_payloadNames = value;
4971             }
4972         }
4973 
4974         /// <summary>
4975         /// Gets the event source object.
4976         /// </summary>
4977         public EventSource EventSource { get { return m_eventSource; } }
4978 
4979         /// <summary>
4980         /// Gets the keywords for the event.
4981         /// </summary>
4982         public EventKeywords Keywords
4983         {
4984             get
4985             {
4986                 if (EventId < 0)      // TraceLogging convention EventID == -1
4987                     return m_keywords;
4988 
4989                 return (EventKeywords)m_eventSource.m_eventData[EventId].Descriptor.Keywords;
4990             }
4991         }
4992 
4993         /// <summary>
4994         /// Gets the operation code for the event.
4995         /// </summary>
4996         public EventOpcode Opcode
4997         {
4998             get
4999             {
5000                 if (EventId <= 0)      // TraceLogging convention EventID == -1
5001                     return m_opcode;
5002                 return (EventOpcode)m_eventSource.m_eventData[EventId].Descriptor.Opcode;
5003             }
5004         }
5005 
5006         /// <summary>
5007         /// Gets the task for the event.
5008         /// </summary>
5009         public EventTask Task
5010         {
5011             get
5012             {
5013                 if (EventId <= 0)      // TraceLogging convention EventID == -1
5014                     return EventTask.None;
5015 
5016                 return (EventTask)m_eventSource.m_eventData[EventId].Descriptor.Task;
5017             }
5018         }
5019 
5020         /// <summary>
5021         /// Any provider/user defined options associated with the event.
5022         /// </summary>
5023         public EventTags Tags
5024         {
5025             get
5026             {
5027                 if (EventId <= 0)      // TraceLogging convention EventID == -1
5028                     return m_tags;
5029                 return m_eventSource.m_eventData[EventId].Tags;
5030             }
5031         }
5032 
5033         /// <summary>
5034         /// Gets the message for the event.  If the message has {N} parameters they are NOT substituted.
5035         /// </summary>
5036         public string Message
5037         {
5038             get
5039             {
5040                 if (EventId <= 0)      // TraceLogging convention EventID == -1
5041                     return m_message;
5042                 else
5043                     return m_eventSource.m_eventData[EventId].Message;
5044             }
5045             internal set
5046             {
5047                 m_message = value;
5048             }
5049         }
5050 
5051 
5052 #if FEATURE_MANAGED_ETW_CHANNELS
5053         /// <summary>
5054         /// Gets the channel for the event.
5055         /// </summary>
5056         public EventChannel Channel
5057         {
5058             get
5059             {
5060                 if (EventId <= 0)      // TraceLogging convention EventID == -1
5061                     return EventChannel.None;
5062                 return (EventChannel)m_eventSource.m_eventData[EventId].Descriptor.Channel;
5063             }
5064         }
5065 #endif
5066 
5067         /// <summary>
5068         /// Gets the version of the event.
5069         /// </summary>
5070         public byte Version
5071         {
5072             get
5073             {
5074                 if (EventId <= 0)      // TraceLogging convention EventID == -1
5075                     return 0;
5076                 return m_eventSource.m_eventData[EventId].Descriptor.Version;
5077             }
5078         }
5079 
5080         /// <summary>
5081         /// Gets the level for the event.
5082         /// </summary>
5083         public EventLevel Level
5084         {
5085             get
5086             {
5087                 if (EventId <= 0)      // TraceLogging convention EventID == -1
5088                     return m_level;
5089                 return (EventLevel)m_eventSource.m_eventData[EventId].Descriptor.Level;
5090             }
5091         }
5092 
5093 #region private
EventWrittenEventArgs(EventSource eventSource)5094         internal EventWrittenEventArgs(EventSource eventSource)
5095         {
5096             m_eventSource = eventSource;
5097         }
5098         private string m_message;
5099         private string m_eventName;
5100         private EventSource m_eventSource;
5101         private ReadOnlyCollection<string> m_payloadNames;
5102         internal EventTags m_tags;
5103         internal EventOpcode m_opcode;
5104         internal EventLevel m_level;
5105         internal EventKeywords m_keywords;
5106 #endregion
5107     }
5108 
5109     /// <summary>
5110     /// Allows customizing defaults and specifying localization support for the event source class to which it is applied.
5111     /// </summary>
5112     [AttributeUsage(AttributeTargets.Class)]
5113     public sealed class EventSourceAttribute : Attribute
5114     {
5115         /// <summary>
5116         /// Overrides the ETW name of the event source (which defaults to the class name)
5117         /// </summary>
5118         public string Name { get; set; }
5119 
5120         /// <summary>
5121         /// Overrides the default (calculated) Guid of an EventSource type. Explicitly defining a GUID is discouraged,
5122         /// except when upgrading existing ETW providers to using event sources.
5123         /// </summary>
5124         public string Guid { get; set; }
5125 
5126         /// <summary>
5127         /// <para>
5128         /// EventSources support localization of events. The names used for events, opcodes, tasks, keywords and maps
5129         /// can be localized to several languages if desired. This works by creating a ResX style string table
5130         /// (by simply adding a 'Resource File' to your project). This resource file is given a name e.g.
5131         /// 'DefaultNameSpace.ResourceFileName' which can be passed to the ResourceManager constructor to read the
5132         /// resources. This name is the value of the LocalizationResources property.
5133         /// </para><para>
5134         /// If LocalizationResources property is non-null, then EventSource will look up the localized strings for events by
5135         /// using the following resource naming scheme
5136         /// </para>
5137         ///     <para>* event_EVENTNAME</para>
5138         ///     <para>* task_TASKNAME</para>
5139         ///     <para>* keyword_KEYWORDNAME</para>
5140         ///     <para>* map_MAPNAME</para>
5141         /// <para>
5142         /// where the capitalized name is the name of the event, task, keyword, or map value that should be localized.
5143         /// Note that the localized string for an event corresponds to the Message string, and can have {0} values
5144         /// which represent the payload values.
5145         /// </para>
5146         /// </summary>
5147         public string LocalizationResources { get; set; }
5148     }
5149 
5150     /// <summary>
5151     /// Any instance methods in a class that subclasses <see cref="EventSource"/> and that return void are
5152     /// assumed by default to be methods that generate an ETW event. Enough information can be deduced from the
5153     /// name of the method and its signature to generate basic schema information for the event. The
5154     /// <see cref="EventAttribute"/> class allows you to specify additional event schema information for an event if
5155     /// desired.
5156     /// </summary>
5157     [AttributeUsage(AttributeTargets.Method)]
5158     public sealed class EventAttribute : Attribute
5159     {
5160         /// <summary>Construct an EventAttribute with specified eventId</summary>
5161         /// <param name="eventId">ID of the ETW event (an integer between 1 and 65535)</param>
EventAttribute(int eventId)5162         public EventAttribute(int eventId) { this.EventId = eventId; Level = EventLevel.Informational; this.m_opcodeSet = false; }
5163         /// <summary>Event's ID</summary>
5164         public int EventId { get; private set; }
5165         /// <summary>Event's severity level: indicates the severity or verbosity of the event</summary>
5166         public EventLevel Level { get; set; }
5167         /// <summary>Event's keywords: allows classification of events by "categories"</summary>
5168         public EventKeywords Keywords { get; set; }
5169         /// <summary>Event's operation code: allows defining operations, generally used with Tasks</summary>
5170         public EventOpcode Opcode
5171         {
5172             get
5173             {
5174                 return m_opcode;
5175             }
5176             set
5177             {
5178                 this.m_opcode = value;
5179                 this.m_opcodeSet = true;
5180             }
5181         }
5182 
5183         internal bool IsOpcodeSet
5184         {
5185             get
5186             {
5187                 return m_opcodeSet;
5188             }
5189         }
5190 
5191         /// <summary>Event's task: allows logical grouping of events</summary>
5192         public EventTask Task { get; set; }
5193 #if FEATURE_MANAGED_ETW_CHANNELS
5194         /// <summary>Event's channel: defines an event log as an additional destination for the event</summary>
5195         public EventChannel Channel { get; set; }
5196 #endif
5197         /// <summary>Event's version</summary>
5198         public byte Version { get; set; }
5199 
5200         /// <summary>
5201         /// This can be specified to enable formatting and localization of the event's payload. You can
5202         /// use standard .NET substitution operators (eg {1}) in the string and they will be replaced
5203         /// with the 'ToString()' of the corresponding part of the  event payload.
5204         /// </summary>
5205         public string Message { get; set; }
5206 
5207         /// <summary>
5208         /// User defined options associated with the event.  These do not have meaning to the EventSource but
5209         /// are passed through to listeners which given them semantics.
5210         /// </summary>
5211         public EventTags Tags { get; set; }
5212 
5213         /// <summary>
5214         /// Allows fine control over the Activity IDs generated by start and stop events
5215         /// </summary>
5216         public EventActivityOptions ActivityOptions { get; set; }
5217 
5218 #region private
5219         EventOpcode m_opcode;
5220         private bool m_opcodeSet;
5221 #endregion
5222     }
5223 
5224     /// <summary>
5225     /// By default all instance methods in a class that subclasses code:EventSource that and return
5226     /// void are assumed to be methods that generate an event. This default can be overridden by specifying
5227     /// the code:NonEventAttribute
5228     /// </summary>
5229     [AttributeUsage(AttributeTargets.Method)]
5230     public sealed class NonEventAttribute : Attribute
5231     {
5232         /// <summary>
5233         /// Constructs a default NonEventAttribute
5234         /// </summary>
NonEventAttribute()5235         public NonEventAttribute() { }
5236     }
5237 
5238     // FUTURE we may want to expose this at some point once we have a partner that can help us validate the design.
5239 #if FEATURE_MANAGED_ETW_CHANNELS
5240     /// <summary>
5241     /// EventChannelAttribute allows customizing channels supported by an EventSource. This attribute must be
5242     /// applied to an member of type EventChannel defined in a Channels class nested in the EventSource class:
5243     /// <code>
5244     ///     public static class Channels
5245     ///     {
5246     ///         [Channel(Enabled = true, EventChannelType = EventChannelType.Admin)]
5247     ///         public const EventChannel Admin = (EventChannel)16;
5248     ///
5249     ///         [Channel(Enabled = false, EventChannelType = EventChannelType.Operational)]
5250     ///         public const EventChannel Operational = (EventChannel)17;
5251     ///     }
5252     /// </code>
5253     /// </summary>
5254     [AttributeUsage(AttributeTargets.Field)]
5255 #if FEATURE_ADVANCED_MANAGED_ETW_CHANNELS
5256     public
5257 #endif
5258     class EventChannelAttribute : Attribute
5259     {
5260         /// <summary>
5261         /// Specified whether the channel is enabled by default
5262         /// </summary>
5263         public bool Enabled { get; set; }
5264 
5265         /// <summary>
5266         /// Legal values are in EventChannelType
5267         /// </summary>
5268         public EventChannelType EventChannelType { get; set; }
5269 
5270 #if FEATURE_ADVANCED_MANAGED_ETW_CHANNELS
5271         /// <summary>
5272         /// Specifies the isolation for the channel
5273         /// </summary>
5274         public EventChannelIsolation Isolation { get; set; }
5275 
5276         /// <summary>
5277         /// Specifies an SDDL access descriptor that controls access to the log file that backs the channel.
5278         /// See MSDN ((http://msdn.microsoft.com/en-us/library/windows/desktop/aa382741.aspx) for details.
5279         /// </summary>
5280         public string Access { get; set; }
5281 
5282         /// <summary>
5283         /// Allows importing channels defined in external manifests
5284         /// </summary>
5285         public string ImportChannel { get; set; }
5286 #endif
5287 
5288         // TODO: there is a convention that the name is the Provider/Type   Should we provide an override?
5289         // public string Name { get; set; }
5290     }
5291 
5292     /// <summary>
5293     /// Allowed channel types
5294     /// </summary>
5295 #if FEATURE_ADVANCED_MANAGED_ETW_CHANNELS
5296     public
5297 #endif
5298     enum EventChannelType
5299     {
5300         /// <summary>The admin channel</summary>
5301         Admin = 1,
5302         /// <summary>The operational channel</summary>
5303         Operational,
5304         /// <summary>The Analytic channel</summary>
5305         Analytic,
5306         /// <summary>The debug channel</summary>
5307         Debug,
5308     }
5309 
5310 #if FEATURE_ADVANCED_MANAGED_ETW_CHANNELS
5311     /// <summary>
5312     /// Allowed isolation levels. See MSDN (http://msdn.microsoft.com/en-us/library/windows/desktop/aa382741.aspx)
5313     /// for the default permissions associated with each level. EventChannelIsolation and Access allows control over the
5314     /// access permissions for the channel and backing file.
5315     /// </summary>
5316     public
5317     enum EventChannelIsolation
5318     {
5319         /// <summary>
5320         /// This is the default isolation level. All channels that specify Application isolation use the same ETW session
5321         /// </summary>
5322         Application = 1,
5323         /// <summary>
5324         /// All channels that specify System isolation use the same ETW session
5325         /// </summary>
5326         System,
5327         /// <summary>
5328         /// Use sparingly! When specifying Custom isolation, a separate ETW session is created for the channel.
5329         /// Using Custom isolation lets you control the access permissions for the channel and backing file.
5330         /// Because there are only 64 ETW sessions available, you should limit your use of Custom isolation.
5331         /// </summary>
5332         Custom,
5333     }
5334 #endif
5335 #endif
5336 
5337     /// <summary>
5338     /// Describes the pre-defined command (EventCommandEventArgs.Command property) that is passed to the OnEventCommand callback.
5339     /// </summary>
5340     public enum EventCommand
5341     {
5342         /// <summary>
5343         /// Update EventSource state
5344         /// </summary>
5345         Update = 0,
5346         /// <summary>
5347         /// Request EventSource to generate and send its manifest
5348         /// </summary>
5349         SendManifest = -1,
5350         /// <summary>
5351         /// Enable event
5352         /// </summary>
5353         Enable = -2,
5354         /// <summary>
5355         /// Disable event
5356         /// </summary>
5357         Disable = -3
5358     };
5359 
5360 
5361 #region private classes
5362 
5363 #if FEATURE_ACTIVITYSAMPLING
5364 
5365     /// <summary>
5366     /// ActivityFilter is a helper structure that is used to keep track of run-time state
5367     /// associated with activity filtering. It is 1-1 with EventListeners (logically
5368     /// every listener has one of these, however we actually allocate them lazily), as well
5369     /// as 1-to-1 with tracing-aware EtwSessions.
5370     ///
5371     /// This structure also keeps track of the sampling counts associated with 'trigger'
5372     /// events.  Because these trigger events are rare, and you typically only have one of
5373     /// them, we store them here as a linked list.
5374     /// </summary>
5375     internal sealed class ActivityFilter : IDisposable
5376     {
5377         /// <summary>
5378         /// Disable all activity filtering for the listener associated with 'filterList',
5379         /// (in the session associated with it) that is triggered by any event in 'source'.
5380         /// </summary>
DisableFilter(ref ActivityFilter filterList, EventSource source)5381         public static void DisableFilter(ref ActivityFilter filterList, EventSource source)
5382         {
5383 #if !ES_BUILD_STANDALONE
5384             Debug.Assert(Monitor.IsEntered(EventListener.EventListenersLock));
5385 #endif
5386 
5387             if (filterList == null)
5388                 return;
5389 
5390             ActivityFilter cur;
5391             // Remove it from anywhere in the list (except the first element, which has to
5392             // be treated specially)
5393             ActivityFilter prev = filterList;
5394             cur = prev.m_next;
5395             while (cur != null)
5396             {
5397                 if (cur.m_providerGuid == source.Guid)
5398                 {
5399                     // update TriggersActivityTracking bit
5400                     if (cur.m_eventId >= 0 && cur.m_eventId < source.m_eventData.Length)
5401                         --source.m_eventData[cur.m_eventId].TriggersActivityTracking;
5402 
5403                     // Remove it from the linked list.
5404                     prev.m_next = cur.m_next;
5405                     // dispose of the removed node
5406                     cur.Dispose();
5407                     // update cursor
5408                     cur = prev.m_next;
5409                 }
5410                 else
5411                 {
5412                     // update cursors
5413                     prev = cur;
5414                     cur = prev.m_next;
5415                 }
5416             }
5417 
5418             // Sadly we have to treat the first element specially in linked list removal in C#
5419             if (filterList.m_providerGuid == source.Guid)
5420             {
5421                 // update TriggersActivityTracking bit
5422                 if (filterList.m_eventId >= 0 && filterList.m_eventId < source.m_eventData.Length)
5423                     --source.m_eventData[filterList.m_eventId].TriggersActivityTracking;
5424 
5425                 // We are the first element in the list.
5426                 var first = filterList;
5427                 filterList = first.m_next;
5428                 // dispose of the removed node
5429                 first.Dispose();
5430             }
5431             // the above might have removed the one ActivityFilter in the session that contains the
5432             // cleanup delegate; re-create the delegate if needed
5433             if (filterList != null)
5434             {
5435                 EnsureActivityCleanupDelegate(filterList);
5436             }
5437         }
5438 
5439         /// <summary>
5440         /// Currently this has "override" semantics. We first disable all filters
5441         /// associated with 'source', and next we add new filters for each entry in the
5442         /// string 'startEvents'. participateInSampling specifies whether non-startEvents
5443         /// always trigger or only trigger when current activity is 'active'.
5444         /// </summary>
UpdateFilter( ref ActivityFilter filterList, EventSource source, int perEventSourceSessionId, string startEvents)5445         public static void UpdateFilter(
5446                                     ref ActivityFilter filterList,
5447                                     EventSource source,
5448                                     int perEventSourceSessionId,
5449                                     string startEvents)
5450         {
5451 #if !ES_BUILD_STANDALONE
5452             Debug.Assert(Monitor.IsEntered(EventListener.EventListenersLock));
5453 #endif
5454 
5455             // first remove all filters associated with 'source'
5456             DisableFilter(ref filterList, source);
5457 
5458             if (!string.IsNullOrEmpty(startEvents))
5459             {
5460                 // ActivitySamplingStartEvents is a space-separated list of Event:Frequency pairs.
5461                 // The Event may be specified by name or by ID. Errors in parsing such a pair
5462                 // result in the error being reported to the listeners, and the pair being ignored.
5463                 // E.g. "CustomActivityStart:1000 12:10" specifies that for event CustomActivityStart
5464                 // we should initiate activity tracing once every 1000 events, *and* for event ID 12
5465                 // we should initiate activity tracing once every 10 events.
5466                 string[] activityFilterStrings = startEvents.Split(' ');
5467 
5468                 for (int i = 0; i < activityFilterStrings.Length; ++i)
5469                 {
5470                     string activityFilterString = activityFilterStrings[i];
5471                     int sampleFreq = 1;
5472                     int eventId = -1;
5473                     int colonIdx = activityFilterString.IndexOf(':');
5474                     if (colonIdx < 0)
5475                     {
5476                         source.ReportOutOfBandMessage("ERROR: Invalid ActivitySamplingStartEvent specification: " +
5477                             activityFilterString, false);
5478                         // ignore failure...
5479                         continue;
5480                     }
5481                     string sFreq = activityFilterString.Substring(colonIdx + 1);
5482                     if (!int.TryParse(sFreq, out sampleFreq))
5483                     {
5484                         source.ReportOutOfBandMessage("ERROR: Invalid sampling frequency specification: " + sFreq, false);
5485                         continue;
5486                     }
5487                     activityFilterString = activityFilterString.Substring(0, colonIdx);
5488                     if (!int.TryParse(activityFilterString, out eventId))
5489                     {
5490                         // reset eventId
5491                         eventId = -1;
5492                         // see if it's an event name
5493                         for (int j = 0; j < source.m_eventData.Length; j++)
5494                         {
5495                             EventSource.EventMetadata[] ed = source.m_eventData;
5496                             if (ed[j].Name != null && ed[j].Name.Length == activityFilterString.Length &&
5497                                 string.Compare(ed[j].Name, activityFilterString, StringComparison.OrdinalIgnoreCase) == 0)
5498                             {
5499                                 eventId = ed[j].Descriptor.EventId;
5500                                 break;
5501                             }
5502                         }
5503                     }
5504                     if (eventId < 0 || eventId >= source.m_eventData.Length)
5505                     {
5506                         source.ReportOutOfBandMessage("ERROR: Invalid eventId specification: " + activityFilterString, false);
5507                         continue;
5508                     }
5509                     EnableFilter(ref filterList, source, perEventSourceSessionId, eventId, sampleFreq);
5510                 }
5511             }
5512         }
5513 
5514         /// <summary>
5515         /// Returns the first ActivityFilter from 'filterList' corresponding to 'source'.
5516         /// </summary>
GetFilter(ActivityFilter filterList, EventSource source)5517         public static ActivityFilter GetFilter(ActivityFilter filterList, EventSource source)
5518         {
5519             for (var af = filterList; af != null; af = af.m_next)
5520             {
5521                 if (af.m_providerGuid == source.Guid && af.m_samplingFreq != -1)
5522                     return af;
5523             }
5524             return null;
5525         }
5526 
5527         /// <summary>
5528         /// Returns a session mask representing all sessions in which the activity
5529         /// associated with the current thread is allowed  through the activity filter.
5530         /// If 'triggeringEvent' is true the event MAY be a triggering event. Ideally
5531         /// most of the time this is false as you can guarantee this event is NOT a
5532         /// triggering event. If 'triggeringEvent' is true, then it checks the
5533         /// 'EventSource' and 'eventID' of the event being logged to see if it is actually
5534         /// a trigger. If so it activates the current activity.
5535         ///
5536         /// If 'childActivityID' is present, it will be added to the active set if the
5537         /// current activity is active.
5538         /// </summary>
PassesActivityFilter( ActivityFilter filterList, Guid* childActivityID, bool triggeringEvent, EventSource source, int eventId)5539         unsafe public static bool PassesActivityFilter(
5540                                     ActivityFilter filterList,
5541                                     Guid* childActivityID,
5542                                     bool triggeringEvent,
5543                                     EventSource source,
5544                                     int eventId)
5545         {
5546             Debug.Assert(filterList != null && filterList.m_activeActivities != null);
5547             bool shouldBeLogged = false;
5548             if (triggeringEvent)
5549             {
5550                 for (ActivityFilter af = filterList; af != null; af = af.m_next)
5551                 {
5552                     if (eventId == af.m_eventId && source.Guid == af.m_providerGuid)
5553                     {
5554                         // Update the sampling count with wrap-around
5555                         int curSampleCount, newSampleCount;
5556                         do
5557                         {
5558                             curSampleCount = af.m_curSampleCount;
5559                             if (curSampleCount <= 1)
5560                                 newSampleCount = af.m_samplingFreq;        // Wrap around, counting down to 1
5561                             else
5562                                 newSampleCount = curSampleCount - 1;
5563                         }
5564                         while (Interlocked.CompareExchange(ref af.m_curSampleCount, newSampleCount, curSampleCount) != curSampleCount);
5565                         // If we hit zero, then start tracking the activity.
5566                         if (curSampleCount <= 1)
5567                         {
5568                             Guid currentActivityId = EventSource.InternalCurrentThreadActivityId;
5569                             Tuple<Guid, int> startId;
5570                             // only add current activity if it's not already a root activity
5571                             if (!af.m_rootActiveActivities.TryGetValue(currentActivityId, out startId))
5572                             {
5573                                 // EventSource.OutputDebugString(string.Format("  PassesAF - Triggering(session {0}, evt {1})", af.m_perEventSourceSessionId, eventId));
5574                                 shouldBeLogged = true;
5575                                 af.m_activeActivities[currentActivityId] = Environment.TickCount;
5576                                 af.m_rootActiveActivities[currentActivityId] = Tuple.Create(source.Guid, eventId);
5577                             }
5578                         }
5579                         else
5580                         {
5581                             // a start event following a triggering start event
5582                             Guid currentActivityId = EventSource.InternalCurrentThreadActivityId;
5583                             Tuple<Guid, int> startId;
5584                             // only remove current activity if we added it
5585                             if (af.m_rootActiveActivities.TryGetValue(currentActivityId, out startId) &&
5586                                 startId.Item1 == source.Guid && startId.Item2 == eventId)
5587                             {
5588                                 // EventSource.OutputDebugString(string.Format("Activity dying: {0} -> StartEvent({1})", currentActivityId, eventId));
5589                                 // remove activity only from current logging scope (af)
5590                                 int dummy;
5591                                 af.m_activeActivities.TryRemove(currentActivityId, out dummy);
5592                             }
5593                         }
5594                         break;
5595                     }
5596                 }
5597             }
5598 
5599             var activeActivities = GetActiveActivities(filterList);
5600             if (activeActivities != null)
5601             {
5602                 // if we hadn't already determined this should be logged, test further
5603                 if (!shouldBeLogged)
5604                 {
5605                     shouldBeLogged = !activeActivities.IsEmpty &&
5606                                      activeActivities.ContainsKey(EventSource.InternalCurrentThreadActivityId);
5607                 }
5608                 if (shouldBeLogged && childActivityID != null &&
5609                     ((EventOpcode)source.m_eventData[eventId].Descriptor.Opcode == EventOpcode.Send))
5610                 {
5611                     FlowActivityIfNeeded(filterList, null, childActivityID);
5612                     // EventSource.OutputDebugString(string.Format("  PassesAF - activity {0}", *childActivityID));
5613                 }
5614             }
5615             // EventSource.OutputDebugString(string.Format("  PassesAF - shouldBeLogged(evt {0}) = {1:x}", eventId, shouldBeLogged));
5616             return shouldBeLogged;
5617         }
5618 
IsCurrentActivityActive(ActivityFilter filterList)5619         public static bool IsCurrentActivityActive(ActivityFilter filterList)
5620         {
5621             var activeActivities = GetActiveActivities(filterList);
5622             if (activeActivities != null &&
5623                 activeActivities.ContainsKey(EventSource.InternalCurrentThreadActivityId))
5624                 return true;
5625 
5626             return false;
5627         }
5628 
5629         /// <summary>
5630         /// For the EventListener/EtwSession associated with 'filterList', add 'childActivityid'
5631         /// to list of active activities IF 'currentActivityId' is also active. Passing in a null
5632         /// value for  'currentActivityid' is an indication the caller has already verified
5633         /// that the current activity is active.
5634         /// </summary>
FlowActivityIfNeeded(ActivityFilter filterList, Guid* currentActivityId, Guid* childActivityID)5635         unsafe public static void FlowActivityIfNeeded(ActivityFilter filterList, Guid* currentActivityId, Guid* childActivityID)
5636         {
5637             Debug.Assert(childActivityID != null);
5638 
5639             var activeActivities = GetActiveActivities(filterList);
5640             Debug.Assert(activeActivities != null);
5641 
5642             // take currentActivityId == null to mean we *know* the current activity is "active"
5643             if (currentActivityId != null && !activeActivities.ContainsKey(*currentActivityId))
5644                 return;
5645 
5646             if (activeActivities.Count > MaxActivityTrackCount)
5647             {
5648                 TrimActiveActivityStore(activeActivities);
5649                 // make sure current activity is still in the set:
5650                 activeActivities[EventSource.InternalCurrentThreadActivityId] = Environment.TickCount;
5651             }
5652             // add child activity to list of activities
5653             activeActivities[*childActivityID] = Environment.TickCount;
5654 
5655         }
5656 
5657         /// <summary>
5658         /// </summary>
UpdateKwdTriggers(ActivityFilter activityFilter, Guid sourceGuid, EventSource source, EventKeywords sessKeywords)5659         public static void UpdateKwdTriggers(ActivityFilter activityFilter, Guid sourceGuid, EventSource source, EventKeywords sessKeywords)
5660         {
5661             for (var af = activityFilter; af != null; af = af.m_next)
5662             {
5663                 if ((sourceGuid == af.m_providerGuid) &&
5664                     (source.m_eventData[af.m_eventId].TriggersActivityTracking > 0 ||
5665                     ((EventOpcode)source.m_eventData[af.m_eventId].Descriptor.Opcode == EventOpcode.Send)))
5666                 {
5667                     // we could be more precise here, if we tracked 'anykeywords' per session
5668                     unchecked
5669                     {
5670                         source.m_keywordTriggers |= (source.m_eventData[af.m_eventId].Descriptor.Keywords & (long)sessKeywords);
5671                     }
5672                 }
5673             }
5674         }
5675 
5676         /// <summary>
5677         /// For the EventSource specified by 'sourceGuid' and the EventListener/EtwSession
5678         /// associated with 'this' ActivityFilter list, return configured sequence of
5679         /// [eventId, sampleFreq] pairs that defines the sampling policy.
5680         /// </summary>
GetFilterAsTuple(Guid sourceGuid)5681         public IEnumerable<Tuple<int, int>> GetFilterAsTuple(Guid sourceGuid)
5682         {
5683             for (ActivityFilter af = this; af != null; af = af.m_next)
5684             {
5685                 if (af.m_providerGuid == sourceGuid)
5686                     yield return Tuple.Create(af.m_eventId, af.m_samplingFreq);
5687             }
5688         }
5689 
5690         /// <summary>
5691         /// The cleanup being performed consists of removing the m_myActivityDelegate from
5692         /// the static s_activityDying, therefore allowing the ActivityFilter to be reclaimed.
5693         /// </summary>
Dispose()5694         public void Dispose()
5695         {
5696 #if !ES_BUILD_STANDALONE
5697             Debug.Assert(Monitor.IsEntered(EventListener.EventListenersLock));
5698 #endif
5699             // m_myActivityDelegate is still alive (held by the static EventSource.s_activityDying).
5700             // Therefore we are ok to take a dependency on m_myActivityDelegate being valid even
5701             // during the finalization of the ActivityFilter
5702             if (m_myActivityDelegate != null)
5703             {
5704                 EventSource.s_activityDying = (Action<Guid>)Delegate.Remove(EventSource.s_activityDying, m_myActivityDelegate);
5705                 m_myActivityDelegate = null;
5706             }
5707         }
5708 
5709 #region private
5710 
5711         /// <summary>
5712         /// Creates a new ActivityFilter that is triggered by 'eventId' from 'source' ever
5713         /// 'samplingFreq' times the event fires. You can have several of these forming a
5714         /// linked list.
5715         /// </summary>
ActivityFilter(EventSource source, int perEventSourceSessionId, int eventId, int samplingFreq, ActivityFilter existingFilter = null)5716         private ActivityFilter(EventSource source, int perEventSourceSessionId, int eventId, int samplingFreq, ActivityFilter existingFilter = null)
5717         {
5718             m_providerGuid = source.Guid;
5719             m_perEventSourceSessionId = perEventSourceSessionId;
5720             m_eventId = eventId;
5721             m_samplingFreq = samplingFreq;
5722             m_next = existingFilter;
5723 
5724             Debug.Assert(existingFilter == null ||
5725                             (existingFilter.m_activeActivities == null) == (existingFilter.m_rootActiveActivities == null));
5726 
5727             // if this is the first filter we add for this session, we need to create a new
5728             // table of activities. m_activeActivities is common across EventSources in the same
5729             // session
5730             ConcurrentDictionary<Guid, int> activeActivities = null;
5731             if (existingFilter == null ||
5732                 (activeActivities = GetActiveActivities(existingFilter)) == null)
5733             {
5734                 m_activeActivities = new ConcurrentDictionary<Guid, int>();
5735                 m_rootActiveActivities = new ConcurrentDictionary<Guid, Tuple<Guid, int>>();
5736 
5737                 // Add a delegate to the 'SetCurrentThreadToActivity callback so that I remove 'dead' activities
5738                 m_myActivityDelegate = GetActivityDyingDelegate(this);
5739                 EventSource.s_activityDying = (Action<Guid>)Delegate.Combine(EventSource.s_activityDying, m_myActivityDelegate);
5740             }
5741             else
5742             {
5743                 m_activeActivities = activeActivities;
5744                 m_rootActiveActivities = existingFilter.m_rootActiveActivities;
5745             }
5746 
5747         }
5748 
5749         /// <summary>
5750         /// Ensure there's at least one ActivityFilter in the 'filterList' that contains an
5751         /// activity-removing delegate for the listener/session associated with 'filterList'.
5752         /// </summary>
EnsureActivityCleanupDelegate(ActivityFilter filterList)5753         private static void EnsureActivityCleanupDelegate(ActivityFilter filterList)
5754         {
5755             if (filterList == null)
5756                 return;
5757 
5758             for (ActivityFilter af = filterList; af != null; af = af.m_next)
5759             {
5760                 if (af.m_myActivityDelegate != null)
5761                     return;
5762             }
5763 
5764             // we didn't find a delegate
5765             filterList.m_myActivityDelegate = GetActivityDyingDelegate(filterList);
5766             EventSource.s_activityDying = (Action<Guid>)Delegate.Combine(EventSource.s_activityDying, filterList.m_myActivityDelegate);
5767         }
5768 
5769         /// <summary>
5770         /// Builds the delegate to be called when an activity is dying. This is responsible
5771         /// for performing whatever cleanup is needed for the ActivityFilter list passed in.
5772         /// This gets "added" to EventSource.s_activityDying and ends up being called from
5773         /// EventSource.SetCurrentThreadActivityId and ActivityFilter.PassesActivityFilter.
5774         /// </summary>
5775         /// <returns>The delegate to be called when an activity is dying</returns>
GetActivityDyingDelegate(ActivityFilter filterList)5776         private static Action<Guid> GetActivityDyingDelegate(ActivityFilter filterList)
5777         {
5778             return (Guid oldActivity) =>
5779             {
5780                 int dummy;
5781                 filterList.m_activeActivities.TryRemove(oldActivity, out dummy);
5782                 Tuple<Guid, int> dummyTuple;
5783                 filterList.m_rootActiveActivities.TryRemove(oldActivity, out dummyTuple);
5784             };
5785         }
5786 
5787         /// <summary>
5788         /// Enables activity filtering for the listener associated with 'filterList', triggering on
5789         /// the event 'eventID' from 'source' with a sampling frequency of 'samplingFreq'
5790         ///
5791         /// if 'eventID' is out of range (e.g. negative), it means we are not triggering (but we are
5792         /// activitySampling if something else triggered).
5793         /// </summary>
5794         /// <returns>true if activity sampling is enabled the samplingFreq is non-zero </returns>
EnableFilter(ref ActivityFilter filterList, EventSource source, int perEventSourceSessionId, int eventId, int samplingFreq)5795         private static bool EnableFilter(ref ActivityFilter filterList, EventSource source, int perEventSourceSessionId, int eventId, int samplingFreq)
5796         {
5797 #if !ES_BUILD_STANDALONE
5798             Debug.Assert(Monitor.IsEntered(EventListener.EventListenersLock));
5799 #endif
5800             Debug.Assert(samplingFreq > 0);
5801             Debug.Assert(eventId >= 0);
5802 
5803             filterList = new ActivityFilter(source, perEventSourceSessionId, eventId, samplingFreq, filterList);
5804 
5805             // Mark the 'quick Check' that indicates this is a trigger event.
5806             // If eventId is out of range then this mark is not done which has the effect of ignoring
5807             // the trigger.
5808             if (0 <= eventId && eventId < source.m_eventData.Length)
5809                 ++source.m_eventData[eventId].TriggersActivityTracking;
5810 
5811             return true;
5812         }
5813 
5814         /// <summary>
5815         /// Normally this code never runs, it is here just to prevent run-away resource usage.
5816         /// </summary>
TrimActiveActivityStore(ConcurrentDictionary<Guid, int> activities)5817         private static void TrimActiveActivityStore(ConcurrentDictionary<Guid, int> activities)
5818         {
5819             if (activities.Count > MaxActivityTrackCount)
5820             {
5821                 // Remove half of the oldest activity ids.
5822                 var keyValues = activities.ToArray();
5823                 var tickNow = Environment.TickCount;
5824 
5825                 // Sort by age, taking into account wrap-around.   As long as x and y are within
5826                 // 23 days of now then (0x7FFFFFFF & (tickNow - x.Value)) is the delta (even if
5827                 // TickCount wraps).  I then sort by DESCENDING age.  (that is oldest value first)
5828                 Array.Sort(keyValues, (x, y) => (0x7FFFFFFF & (tickNow - y.Value)) - (0x7FFFFFFF & (tickNow - x.Value)));
5829                 for (int i = 0; i < keyValues.Length / 2; i++)
5830                 {
5831                     int dummy;
5832                     activities.TryRemove(keyValues[i].Key, out dummy);
5833                 }
5834             }
5835         }
5836 
GetActiveActivities( ActivityFilter filterList)5837         private static ConcurrentDictionary<Guid, int> GetActiveActivities(
5838                                     ActivityFilter filterList)
5839         {
5840             for (ActivityFilter af = filterList; af != null; af = af.m_next)
5841             {
5842                 if (af.m_activeActivities != null)
5843                     return af.m_activeActivities;
5844             }
5845             return null;
5846         }
5847 
5848         // m_activeActivities always points to the sample dictionary for EVERY ActivityFilter
5849         // in the m_next list. The 'int' value in the m_activities set is a timestamp
5850         // (Environment.TickCount) of when the entry was put in the system and is used to
5851         // remove 'old' entries that if the set gets too big.
5852         ConcurrentDictionary<Guid, int> m_activeActivities;
5853 
5854         // m_rootActiveActivities holds the "root" active activities, i.e. the activities
5855         // that were marked as active because a Start event fired on them. We need to keep
5856         // track of these to enable sampling in the scenario of an app's main thread that
5857         // never explicitly sets distinct activity IDs as it executes. To handle these
5858         // situations we manufacture a Guid from the thread's ID, and:
5859         //   (a) we consider the firing of a start event when the sampling counter reaches
5860         //       zero to mark the beginning of an interesting activity, and
5861         //   (b) we consider the very next firing of the same start event to mark the
5862         //       ending of that activity.
5863         // We use a ConcurrentDictionary to avoid taking explicit locks.
5864         //   The key (a guid) represents the activity ID of the root active activity
5865         //   The value is made up of the Guid of the event provider and the eventId of
5866         //      the start event.
5867         ConcurrentDictionary<Guid, Tuple<Guid, int>> m_rootActiveActivities;
5868         Guid m_providerGuid;        // We use the GUID rather than object identity because we don't want to keep the eventSource alive
5869         int m_eventId;              // triggering event
5870         int m_samplingFreq;         // Counter reset to this when it hits 0
5871         int m_curSampleCount;       // We count down to 0 and then activate the activity.
5872         int m_perEventSourceSessionId;  // session ID bit for ETW, 0 for EventListeners
5873 
5874         const int MaxActivityTrackCount = 100000;   // maximum number of tracked activities
5875 
5876         ActivityFilter m_next;      // We create a linked list of these
5877         Action<Guid> m_myActivityDelegate;
5878 #endregion
5879     };
5880 
5881 
5882     /// <summary>
5883     /// An EtwSession instance represents an activity-tracing-aware ETW session. Since these
5884     /// are limited to 8 concurrent sessions per machine (currently) we're going to store
5885     /// the active ones in a singly linked list.
5886     /// </summary>
5887     internal class EtwSession
5888     {
GetEtwSession(int etwSessionId, bool bCreateIfNeeded = false)5889         public static EtwSession GetEtwSession(int etwSessionId, bool bCreateIfNeeded = false)
5890         {
5891             if (etwSessionId < 0)
5892                 return null;
5893 
5894             EtwSession etwSession;
5895             foreach (var wrEtwSession in s_etwSessions)
5896             {
5897 #if ES_BUILD_STANDALONE
5898                 if ((etwSession = (EtwSession) wrEtwSession.Target) != null && etwSession.m_etwSessionId == etwSessionId)
5899                     return etwSession;
5900 #else
5901                 if (wrEtwSession.TryGetTarget(out etwSession) && etwSession.m_etwSessionId == etwSessionId)
5902                     return etwSession;
5903 #endif
5904             }
5905 
5906             if (!bCreateIfNeeded)
5907                 return null;
5908 
5909 #if ES_BUILD_STANDALONE
5910             if (s_etwSessions == null)
5911                 s_etwSessions = new List<WeakReference>();
5912 
5913             etwSession = new EtwSession(etwSessionId);
5914             s_etwSessions.Add(new WeakReference(etwSession));
5915 #else
5916             if (s_etwSessions == null)
5917                 s_etwSessions = new List<WeakReference<EtwSession>>();
5918 
5919             etwSession = new EtwSession(etwSessionId);
5920             s_etwSessions.Add(new WeakReference<EtwSession>(etwSession));
5921 #endif
5922 
5923             if (s_etwSessions.Count > s_thrSessionCount)
5924                 TrimGlobalList();
5925 
5926             return etwSession;
5927 
5928         }
5929 
RemoveEtwSession(EtwSession etwSession)5930         public static void RemoveEtwSession(EtwSession etwSession)
5931         {
5932             Debug.Assert(etwSession != null);
5933             if (s_etwSessions == null || etwSession == null)
5934                 return;
5935 
5936             s_etwSessions.RemoveAll((wrEtwSession) =>
5937                 {
5938                     EtwSession session;
5939 #if ES_BUILD_STANDALONE
5940                     return (session = (EtwSession) wrEtwSession.Target) != null &&
5941                            (session.m_etwSessionId == etwSession.m_etwSessionId);
5942 #else
5943                     return wrEtwSession.TryGetTarget(out session) &&
5944                            (session.m_etwSessionId == etwSession.m_etwSessionId);
5945 #endif
5946                 });
5947 
5948             if (s_etwSessions.Count > s_thrSessionCount)
5949                 TrimGlobalList();
5950         }
5951 
TrimGlobalList()5952         private static void TrimGlobalList()
5953         {
5954             if (s_etwSessions == null)
5955                 return;
5956 
5957             s_etwSessions.RemoveAll((wrEtwSession) =>
5958                 {
5959 #if ES_BUILD_STANDALONE
5960                     return wrEtwSession.Target == null;
5961 #else
5962                     EtwSession session;
5963                     return !wrEtwSession.TryGetTarget(out session);
5964 #endif
5965                 });
5966         }
5967 
EtwSession(int etwSessionId)5968         private EtwSession(int etwSessionId)
5969         {
5970             m_etwSessionId = etwSessionId;
5971         }
5972 
5973         public readonly int m_etwSessionId;        // ETW session ID (as retrieved by EventProvider)
5974         public ActivityFilter m_activityFilter;    // all filters enabled for this session
5975 
5976 #if ES_BUILD_STANDALONE
5977         private static List<WeakReference> s_etwSessions = new List<WeakReference>();
5978 #else
5979         private static List<WeakReference<EtwSession>> s_etwSessions = new List<WeakReference<EtwSession>>();
5980 #endif
5981         private const int s_thrSessionCount = 16;
5982     }
5983 
5984 #endif // FEATURE_ACTIVITYSAMPLING
5985 
5986     // holds a bitfield representing a session mask
5987     /// <summary>
5988     /// A SessionMask represents a set of (at most MAX) sessions as a bit mask. The perEventSourceSessionId
5989     /// is the index in the SessionMask of the bit that will be set. These can translate to
5990     /// EventSource's reserved keywords bits using the provided ToEventKeywords() and
5991     /// FromEventKeywords() methods.
5992     /// </summary>
5993     internal struct SessionMask
5994     {
SessionMaskMicrosoft.Diagnostics.Tracing.SessionMask5995         public SessionMask(SessionMask m)
5996         { m_mask = m.m_mask; }
5997 
SessionMaskMicrosoft.Diagnostics.Tracing.SessionMask5998         public SessionMask(uint mask = 0)
5999         { m_mask = mask & MASK; }
6000 
IsEqualOrSupersetOfMicrosoft.Diagnostics.Tracing.SessionMask6001         public bool IsEqualOrSupersetOf(SessionMask m)
6002         {
6003             return (this.m_mask | m.m_mask) == this.m_mask;
6004         }
6005 
6006         public static SessionMask All
6007         {
6008             get { return new SessionMask(MASK); }
6009         }
6010 
FromIdMicrosoft.Diagnostics.Tracing.SessionMask6011         public static SessionMask FromId(int perEventSourceSessionId)
6012         {
6013             Debug.Assert(perEventSourceSessionId < MAX);
6014             return new SessionMask((uint)1 << perEventSourceSessionId);
6015         }
6016 
ToEventKeywordsMicrosoft.Diagnostics.Tracing.SessionMask6017         public ulong ToEventKeywords()
6018         {
6019             return (ulong)m_mask << SHIFT_SESSION_TO_KEYWORD;
6020         }
6021 
FromEventKeywordsMicrosoft.Diagnostics.Tracing.SessionMask6022         public static SessionMask FromEventKeywords(ulong m)
6023         {
6024             return new SessionMask((uint)(m >> SHIFT_SESSION_TO_KEYWORD));
6025         }
6026 
6027         public bool this[int perEventSourceSessionId]
6028         {
6029             get
6030             {
6031                 Debug.Assert(perEventSourceSessionId < MAX);
6032                 return (m_mask & (1 << perEventSourceSessionId)) != 0;
6033             }
6034             set
6035             {
6036                 Debug.Assert(perEventSourceSessionId < MAX);
6037                 if (value) m_mask |= ((uint)1 << perEventSourceSessionId);
6038                 else m_mask &= ~((uint)1 << perEventSourceSessionId);
6039             }
6040         }
6041 
operator |Microsoft.Diagnostics.Tracing.SessionMask6042         public static SessionMask operator |(SessionMask m1, SessionMask m2)
6043         {
6044             return new SessionMask(m1.m_mask | m2.m_mask);
6045         }
6046 
operator &Microsoft.Diagnostics.Tracing.SessionMask6047         public static SessionMask operator &(SessionMask m1, SessionMask m2)
6048         {
6049             return new SessionMask(m1.m_mask & m2.m_mask);
6050         }
6051 
operator ^Microsoft.Diagnostics.Tracing.SessionMask6052         public static SessionMask operator ^(SessionMask m1, SessionMask m2)
6053         {
6054             return new SessionMask(m1.m_mask ^ m2.m_mask);
6055         }
6056 
operator ~Microsoft.Diagnostics.Tracing.SessionMask6057         public static SessionMask operator ~(SessionMask m)
6058         {
6059             return new SessionMask(MASK & ~(m.m_mask));
6060         }
6061 
operator ulongMicrosoft.Diagnostics.Tracing.SessionMask6062         public static explicit operator ulong(SessionMask m)
6063         { return m.m_mask; }
6064 
operator uintMicrosoft.Diagnostics.Tracing.SessionMask6065         public static explicit operator uint(SessionMask m)
6066         { return m.m_mask; }
6067 
6068         private uint m_mask;
6069 
6070         internal const int SHIFT_SESSION_TO_KEYWORD = 44;         // bits 44-47 inclusive are reserved
6071         internal const uint MASK = 0x0fU;                         // the mask of 4 reserved bits
6072         internal const uint MAX = 4;                              // maximum number of simultaneous ETW sessions supported
6073     }
6074 
6075     /// <summary>
6076     /// code:EventDispatchers are a simple 'helper' structure that holds the filtering state
6077     /// (m_EventEnabled) for a particular EventSource X EventListener tuple
6078     ///
6079     /// Thus a single EventListener may have many EventDispatchers (one for every EventSource
6080     /// that EventListener has activate) and a Single EventSource may also have many
6081     /// event Dispatchers (one for every EventListener that has activated it).
6082     ///
6083     /// Logically a particular EventDispatcher belongs to exactly one EventSource and exactly
6084     /// one EventListener (although EventDispatcher does not 'remember' the EventSource it is
6085     /// associated with.
6086     /// </summary>
6087     internal class EventDispatcher
6088     {
EventDispatcher(EventDispatcher next, bool[] eventEnabled, EventListener listener)6089         internal EventDispatcher(EventDispatcher next, bool[] eventEnabled, EventListener listener)
6090         {
6091             m_Next = next;
6092             m_EventEnabled = eventEnabled;
6093             m_Listener = listener;
6094         }
6095 
6096         // Instance fields
6097         readonly internal EventListener m_Listener;   // The dispatcher this entry is for
6098         internal bool[] m_EventEnabled;               // For every event in a the eventSource, is it enabled?
6099 #if FEATURE_ACTIVITYSAMPLING
6100         internal bool m_activityFilteringEnabled;     // does THIS EventSource have activity filtering turned on for this listener?
6101 #endif // FEATURE_ACTIVITYSAMPLING
6102 
6103         // Only guaranteed to exist after a InsureInit()
6104         internal EventDispatcher m_Next;              // These form a linked list in code:EventSource.m_Dispatchers
6105         // Of all listeners for that eventSource.
6106     }
6107 
6108     /// <summary>
6109     /// Flags that can be used with EventSource.GenerateManifest to control how the ETW manifest for the EventSource is
6110     /// generated.
6111     /// </summary>
6112     [Flags]
6113     public enum EventManifestOptions
6114     {
6115         /// <summary>
6116         /// Only the resources associated with current UI culture are included in the  manifest
6117         /// </summary>
6118         None = 0x0,
6119         /// <summary>
6120         /// Throw exceptions for any inconsistency encountered
6121         /// </summary>
6122         Strict = 0x1,
6123         /// <summary>
6124         /// Generate a "resources" node under "localization" for every satellite assembly provided
6125         /// </summary>
6126         AllCultures = 0x2,
6127         /// <summary>
6128         /// Generate the manifest only if the event source needs to be registered on the machine,
6129         /// otherwise return null (but still perform validation if Strict is specified)
6130         /// </summary>
6131         OnlyIfNeededForRegistration = 0x4,
6132         /// <summary>
6133         /// When generating the manifest do *not* enforce the rule that the current EventSource class
6134         /// must be the base class for the user-defined type passed in. This allows validation of .net
6135         /// event sources using the new validation code
6136         /// </summary>
6137         AllowEventSourceOverride = 0x8,
6138     }
6139 
6140     /// <summary>
6141     /// ManifestBuilder is designed to isolate the details of the message of the event from the
6142     /// rest of EventSource.  This one happens to create XML.
6143     /// </summary>
6144     internal partial class ManifestBuilder
6145     {
6146         /// <summary>
6147         /// Build a manifest for 'providerName' with the given GUID, which will be packaged into 'dllName'.
6148         /// 'resources, is a resource manager.  If specified all messages are localized using that manager.
6149         /// </summary>
ManifestBuilder(string providerName, Guid providerGuid, string dllName, ResourceManager resources, EventManifestOptions flags)6150         public ManifestBuilder(string providerName, Guid providerGuid, string dllName, ResourceManager resources,
6151                                EventManifestOptions flags)
6152         {
6153 #if FEATURE_MANAGED_ETW_CHANNELS
6154             this.providerName = providerName;
6155 #endif
6156             this.flags = flags;
6157 
6158             this.resources = resources;
6159             sb = new StringBuilder();
6160             events = new StringBuilder();
6161             templates = new StringBuilder();
6162             opcodeTab = new Dictionary<int, string>();
6163             stringTab = new Dictionary<string, string>();
6164             errors = new List<string>();
6165             perEventByteArrayArgIndices = new Dictionary<string, List<int>>();
6166 
6167             sb.AppendLine("<instrumentationManifest xmlns=\"http://schemas.microsoft.com/win/2004/08/events\">");
6168             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\">");
6169             sb.AppendLine("  <events xmlns=\"http://schemas.microsoft.com/win/2004/08/events\">");
6170             sb.Append("<provider name=\"").Append(providerName).
6171                Append("\" guid=\"{").Append(providerGuid.ToString()).Append("}");
6172             if (dllName != null)
6173                 sb.Append("\" resourceFileName=\"").Append(dllName).Append("\" messageFileName=\"").Append(dllName);
6174 
6175             var symbolsName = providerName.Replace("-", "").Replace(".", "_");  // Period and - are illegal replace them.
6176             sb.Append("\" symbol=\"").Append(symbolsName);
6177             sb.Append("\">").AppendLine();
6178         }
6179 
AddOpcode(string name, int value)6180         public void AddOpcode(string name, int value)
6181         {
6182             if ((flags & EventManifestOptions.Strict) != 0)
6183             {
6184                 if (value <= 10 || value >= 239)
6185                 {
6186                     ManifestError(SR.Format(SR.EventSource_IllegalOpcodeValue, name, value));
6187                 }
6188                 string prevName;
6189                 if (opcodeTab.TryGetValue(value, out prevName) && !name.Equals(prevName, StringComparison.Ordinal))
6190                 {
6191                     ManifestError(SR.Format(SR.EventSource_OpcodeCollision, name, prevName, value));
6192                 }
6193             }
6194             opcodeTab[value] = name;
6195         }
AddTask(string name, int value)6196         public void AddTask(string name, int value)
6197         {
6198             if ((flags & EventManifestOptions.Strict) != 0)
6199             {
6200                 if (value <= 0 || value >= 65535)
6201                 {
6202                     ManifestError(SR.Format(SR.EventSource_IllegalTaskValue, name, value));
6203                 }
6204                 string prevName;
6205                 if (taskTab != null && taskTab.TryGetValue(value, out prevName) && !name.Equals(prevName, StringComparison.Ordinal))
6206                 {
6207                     ManifestError(SR.Format(SR.EventSource_TaskCollision, name, prevName, value));
6208                 }
6209             }
6210             if (taskTab == null)
6211                 taskTab = new Dictionary<int, string>();
6212             taskTab[value] = name;
6213         }
AddKeyword(string name, ulong value)6214         public void AddKeyword(string name, ulong value)
6215         {
6216             if ((value & (value - 1)) != 0)   // Is it a power of 2?
6217             {
6218                 ManifestError(SR.Format(SR.EventSource_KeywordNeedPowerOfTwo, "0x" + value.ToString("x", CultureInfo.CurrentCulture), name), true);
6219             }
6220             if ((flags & EventManifestOptions.Strict) != 0)
6221             {
6222                 if (value >= 0x0000100000000000UL && !name.StartsWith("Session", StringComparison.Ordinal))
6223                 {
6224                     ManifestError(SR.Format(SR.EventSource_IllegalKeywordsValue, name, "0x" + value.ToString("x", CultureInfo.CurrentCulture)));
6225                 }
6226                 string prevName;
6227                 if (keywordTab != null && keywordTab.TryGetValue(value, out prevName) && !name.Equals(prevName, StringComparison.Ordinal))
6228                 {
6229                     ManifestError(SR.Format(SR.EventSource_KeywordCollision, name, prevName, "0x" + value.ToString("x", CultureInfo.CurrentCulture)));
6230                 }
6231             }
6232             if (keywordTab == null)
6233                 keywordTab = new Dictionary<ulong, string>();
6234             keywordTab[value] = name;
6235         }
6236 
6237 #if FEATURE_MANAGED_ETW_CHANNELS
6238         /// <summary>
6239         /// Add a channel.  channelAttribute can be null
6240         /// </summary>
AddChannel(string name, int value, EventChannelAttribute channelAttribute)6241         public void AddChannel(string name, int value, EventChannelAttribute channelAttribute)
6242         {
6243             EventChannel chValue = (EventChannel)value;
6244             if (value < (int)EventChannel.Admin || value > 255)
6245                 ManifestError(SR.Format(SR.EventSource_EventChannelOutOfRange, name, value));
6246             else if (chValue >= EventChannel.Admin && chValue <= EventChannel.Debug &&
6247                      channelAttribute != null && EventChannelToChannelType(chValue) != channelAttribute.EventChannelType)
6248             {
6249                 // we want to ensure developers do not define EventChannels that conflict with the builtin ones,
6250                 // but we want to allow them to override the default ones...
6251                 ManifestError(SR.Format(SR.EventSource_ChannelTypeDoesNotMatchEventChannelValue,
6252                                                                             name, ((EventChannel)value).ToString()));
6253             }
6254 
6255             // TODO: validate there are no conflicting manifest exposed names (generally following the format "provider/type")
6256 
6257             ulong kwd = GetChannelKeyword(chValue);
6258 
6259             if (channelTab == null)
6260                 channelTab = new Dictionary<int, ChannelInfo>(4);
6261             channelTab[value] = new ChannelInfo { Name = name, Keywords = kwd, Attribs = channelAttribute };
6262         }
6263 
EventChannelToChannelType(EventChannel channel)6264         private EventChannelType EventChannelToChannelType(EventChannel channel)
6265         {
6266 #if !ES_BUILD_STANDALONE
6267             Debug.Assert(channel >= EventChannel.Admin && channel <= EventChannel.Debug);
6268 #endif
6269             return (EventChannelType)((int)channel - (int)EventChannel.Admin + (int)EventChannelType.Admin);
6270         }
GetDefaultChannelAttribute(EventChannel channel)6271         private EventChannelAttribute GetDefaultChannelAttribute(EventChannel channel)
6272         {
6273             EventChannelAttribute attrib = new EventChannelAttribute();
6274             attrib.EventChannelType = EventChannelToChannelType(channel);
6275             if (attrib.EventChannelType <= EventChannelType.Operational)
6276                 attrib.Enabled = true;
6277             return attrib;
6278         }
6279 
GetChannelData()6280         public ulong[] GetChannelData()
6281         {
6282             if (this.channelTab == null)
6283             {
6284                 return new ulong[0];
6285             }
6286 
6287             // We create an array indexed by the channel id for fast look up.
6288             // E.g. channelMask[Admin] will give you the bit mask for Admin channel.
6289             int maxkey = -1;
6290             foreach (var item in this.channelTab.Keys)
6291             {
6292                 if (item > maxkey)
6293                 {
6294                     maxkey = item;
6295                 }
6296             }
6297 
6298             ulong[] channelMask = new ulong[maxkey + 1];
6299             foreach (var item in this.channelTab)
6300             {
6301                 channelMask[item.Key] = item.Value.Keywords;
6302             }
6303 
6304             return channelMask;
6305         }
6306 
6307 #endif
StartEvent(string eventName, EventAttribute eventAttribute)6308         public void StartEvent(string eventName, EventAttribute eventAttribute)
6309         {
6310             Debug.Assert(numParams == 0);
6311             Debug.Assert(this.eventName == null);
6312             this.eventName = eventName;
6313             numParams = 0;
6314             byteArrArgIndices = null;
6315 
6316             events.Append("  <event").
6317                  Append(" value=\"").Append(eventAttribute.EventId).Append("\"").
6318                  Append(" version=\"").Append(eventAttribute.Version).Append("\"").
6319                  Append(" level=\"").Append(GetLevelName(eventAttribute.Level)).Append("\"").
6320                  Append(" symbol=\"").Append(eventName).Append("\"");
6321 
6322             // at this point we add to the manifest's stringTab a message that is as-of-yet
6323             // "untranslated to manifest convention", b/c we don't have the number or position
6324             // of any byte[] args (which require string format index updates)
6325             WriteMessageAttrib(events, "event", eventName, eventAttribute.Message);
6326 
6327             if (eventAttribute.Keywords != 0)
6328                 events.Append(" keywords=\"").Append(GetKeywords((ulong)eventAttribute.Keywords, eventName)).Append("\"");
6329             if (eventAttribute.Opcode != 0)
6330                 events.Append(" opcode=\"").Append(GetOpcodeName(eventAttribute.Opcode, eventName)).Append("\"");
6331             if (eventAttribute.Task != 0)
6332                 events.Append(" task=\"").Append(GetTaskName(eventAttribute.Task, eventName)).Append("\"");
6333 #if FEATURE_MANAGED_ETW_CHANNELS
6334             if (eventAttribute.Channel != 0)
6335             {
6336                 events.Append(" channel=\"").Append(GetChannelName(eventAttribute.Channel, eventName, eventAttribute.Message)).Append("\"");
6337             }
6338 #endif
6339         }
6340 
AddEventParameter(Type type, string name)6341         public void AddEventParameter(Type type, string name)
6342         {
6343             if (numParams == 0)
6344                 templates.Append("  <template tid=\"").Append(eventName).Append("Args\">").AppendLine();
6345             if (type == typeof(byte[]))
6346             {
6347                 // mark this index as "extraneous" (it has no parallel in the managed signature)
6348                 // we use these values in TranslateToManifestConvention()
6349                 if (byteArrArgIndices == null)
6350                     byteArrArgIndices = new List<int>(4);
6351                 byteArrArgIndices.Add(numParams);
6352 
6353                 // add an extra field to the template representing the length of the binary blob
6354                 numParams++;
6355                 templates.Append("   <data name=\"").Append(name).Append("Size\" inType=\"win:UInt32\"/>").AppendLine();
6356             }
6357             numParams++;
6358             templates.Append("   <data name=\"").Append(name).Append("\" inType=\"").Append(GetTypeName(type)).Append("\"");
6359             // TODO: for 'byte*' types it assumes the user provided length is named using the same naming convention
6360             //       as for 'byte[]' args (blob_arg_name + "Size")
6361             if ((type.IsArray || type.IsPointer) && type.GetElementType() == typeof(byte))
6362             {
6363                 // add "length" attribute to the "blob" field in the template (referencing the field added above)
6364                 templates.Append(" length=\"").Append(name).Append("Size\"");
6365             }
6366             // ETW does not support 64-bit value maps, so we don't specify these as ETW maps
6367             if (type.IsEnum() && Enum.GetUnderlyingType(type) != typeof(UInt64) && Enum.GetUnderlyingType(type) != typeof(Int64))
6368             {
6369                 templates.Append(" map=\"").Append(type.Name).Append("\"");
6370                 if (mapsTab == null)
6371                     mapsTab = new Dictionary<string, Type>();
6372                 if (!mapsTab.ContainsKey(type.Name))
6373                     mapsTab.Add(type.Name, type);        // Remember that we need to dump the type enumeration
6374             }
6375 
6376             templates.Append("/>").AppendLine();
6377         }
EndEvent()6378         public void EndEvent()
6379         {
6380             if (numParams > 0)
6381             {
6382                 templates.Append("  </template>").AppendLine();
6383                 events.Append(" template=\"").Append(eventName).Append("Args\"");
6384             }
6385             events.Append("/>").AppendLine();
6386 
6387             if (byteArrArgIndices != null)
6388                 perEventByteArrayArgIndices[eventName] = byteArrArgIndices;
6389 
6390             // at this point we have all the information we need to translate the C# Message
6391             // to the manifest string we'll put in the stringTab
6392             string msg;
6393             if (stringTab.TryGetValue("event_" + eventName, out msg))
6394             {
6395                 msg = TranslateToManifestConvention(msg, eventName);
6396                 stringTab["event_" + eventName] = msg;
6397             }
6398 
6399             eventName = null;
6400             numParams = 0;
6401             byteArrArgIndices = null;
6402         }
6403 
6404 #if FEATURE_MANAGED_ETW_CHANNELS
6405         // Channel keywords are generated one per channel to allow channel based filtering in event viewer. These keywords are autogenerated
6406         // by mc.exe for compiling a manifest and are based on the order of the channels (fields) in the Channels inner class (when advanced
6407         // channel support is enabled), or based on the order the predefined channels appear in the EventAttribute properties (for simple
6408         // support). The manifest generated *MUST* have the channels specified in the same order (that's how our computed keywords are mapped
6409         // to channels by the OS infrastructure).
6410         // If channelKeyworkds is present, and has keywords bits in the ValidPredefinedChannelKeywords then it is
6411         // assumed that that the keyword for that channel should be that bit.
6412         // otherwise we allocate a channel bit for the channel.
6413         // explicit channel bits are only used by WCF to mimic an existing manifest,
6414         // so we don't dont do error checking.
GetChannelKeyword(EventChannel channel, ulong channelKeyword = 0)6415         public ulong GetChannelKeyword(EventChannel channel, ulong channelKeyword = 0)
6416         {
6417             // strip off any non-channel keywords, since we are only interested in channels here.
6418             channelKeyword &= ValidPredefinedChannelKeywords;
6419             if (channelTab == null)
6420             {
6421                 channelTab = new Dictionary<int, ChannelInfo>(4);
6422             }
6423 
6424             if (channelTab.Count == MaxCountChannels)
6425                 ManifestError(SR.EventSource_MaxChannelExceeded);
6426 
6427             ChannelInfo info;
6428             if (!channelTab.TryGetValue((int)channel, out info))
6429             {
6430                 // If we were not given an explicit channel, allocate one.
6431                 if (channelKeyword != 0)
6432                 {
6433                     channelKeyword = nextChannelKeywordBit;
6434                     nextChannelKeywordBit >>= 1;
6435                 }
6436             }
6437             else
6438             {
6439                 channelKeyword = info.Keywords;
6440             }
6441 
6442             return channelKeyword;
6443         }
6444 #endif
6445 
CreateManifest()6446         public byte[] CreateManifest()
6447         {
6448             string str = CreateManifestString();
6449             return Encoding.UTF8.GetBytes(str);
6450         }
6451 
6452         public IList<string> Errors { get { return errors; } }
6453 
6454         /// <summary>
6455         /// When validating an event source it adds the error to the error collection.
6456         /// When not validating it throws an exception if runtimeCritical is "true".
6457         /// Otherwise the error is ignored.
6458         /// </summary>
6459         /// <param name="msg"></param>
6460         /// <param name="runtimeCritical"></param>
ManifestError(string msg, bool runtimeCritical = false)6461         public void ManifestError(string msg, bool runtimeCritical = false)
6462         {
6463             if ((flags & EventManifestOptions.Strict) != 0)
6464                 errors.Add(msg);
6465             else if (runtimeCritical)
6466                 throw new ArgumentException(msg);
6467         }
6468 
CreateManifestString()6469         private string CreateManifestString()
6470         {
6471 
6472 #if FEATURE_MANAGED_ETW_CHANNELS
6473             // Write out the channels
6474             if (channelTab != null)
6475             {
6476                 sb.Append(" <channels>").AppendLine();
6477                 var sortedChannels = new List<KeyValuePair<int, ChannelInfo>>();
6478                 foreach (KeyValuePair<int, ChannelInfo> p in channelTab) { sortedChannels.Add(p); }
6479                 sortedChannels.Sort((p1, p2) => -Comparer<ulong>.Default.Compare(p1.Value.Keywords, p2.Value.Keywords));
6480                 foreach (var kvpair in sortedChannels)
6481                 {
6482                     int channel = kvpair.Key;
6483                     ChannelInfo channelInfo = kvpair.Value;
6484 
6485                     string channelType = null;
6486                     string elementName = "channel";
6487                     bool enabled = false;
6488                     string fullName = null;
6489 #if FEATURE_ADVANCED_MANAGED_ETW_CHANNELS
6490                     string isolation = null;
6491                     string access = null;
6492 #endif
6493                     if (channelInfo.Attribs != null)
6494                     {
6495                         var attribs = channelInfo.Attribs;
6496                         if (Enum.IsDefined(typeof(EventChannelType), attribs.EventChannelType))
6497                             channelType = attribs.EventChannelType.ToString();
6498                         enabled = attribs.Enabled;
6499 #if FEATURE_ADVANCED_MANAGED_ETW_CHANNELS
6500                         if (attribs.ImportChannel != null)
6501                         {
6502                             fullName = attribs.ImportChannel;
6503                             elementName = "importChannel";
6504                         }
6505                         if (Enum.IsDefined(typeof(EventChannelIsolation), attribs.Isolation))
6506                             isolation = attribs.Isolation.ToString();
6507                         access = attribs.Access;
6508 #endif
6509                     }
6510                     if (fullName == null)
6511                         fullName = providerName + "/" + channelInfo.Name;
6512 
6513                     sb.Append("  <").Append(elementName);
6514                     sb.Append(" chid=\"").Append(channelInfo.Name).Append("\"");
6515                     sb.Append(" name=\"").Append(fullName).Append("\"");
6516                     if (elementName == "channel")   // not applicable to importChannels.
6517                     {
6518                         WriteMessageAttrib(sb, "channel", channelInfo.Name, null);
6519                         sb.Append(" value=\"").Append(channel).Append("\"");
6520                         if (channelType != null)
6521                             sb.Append(" type=\"").Append(channelType).Append("\"");
6522                         sb.Append(" enabled=\"").Append(enabled.ToString().ToLower()).Append("\"");
6523 #if FEATURE_ADVANCED_MANAGED_ETW_CHANNELS
6524                         if (access != null)
6525                             sb.Append(" access=\"").Append(access).Append("\"");
6526                         if (isolation != null)
6527                             sb.Append(" isolation=\"").Append(isolation).Append("\"");
6528 #endif
6529                     }
6530                     sb.Append("/>").AppendLine();
6531                 }
6532                 sb.Append(" </channels>").AppendLine();
6533             }
6534 #endif
6535 
6536             // Write out the tasks
6537             if (taskTab != null)
6538             {
6539 
6540                 sb.Append(" <tasks>").AppendLine();
6541                 var sortedTasks = new List<int>(taskTab.Keys);
6542                 sortedTasks.Sort();
6543                 foreach (int task in sortedTasks)
6544                 {
6545                     sb.Append("  <task");
6546                     WriteNameAndMessageAttribs(sb, "task", taskTab[task]);
6547                     sb.Append(" value=\"").Append(task).Append("\"/>").AppendLine();
6548                 }
6549                 sb.Append(" </tasks>").AppendLine();
6550             }
6551 
6552             // Write out the maps
6553             if (mapsTab != null)
6554             {
6555                 sb.Append(" <maps>").AppendLine();
6556                 foreach (Type enumType in mapsTab.Values)
6557                 {
6558                     bool isbitmap = EventSource.GetCustomAttributeHelper(enumType, typeof(FlagsAttribute), flags) != null;
6559                     string mapKind = isbitmap ? "bitMap" : "valueMap";
6560                     sb.Append("  <").Append(mapKind).Append(" name=\"").Append(enumType.Name).Append("\">").AppendLine();
6561 
6562                     // write out each enum value
6563                     FieldInfo[] staticFields = enumType.GetFields(BindingFlags.DeclaredOnly | BindingFlags.Public | BindingFlags.Static);
6564                     foreach (FieldInfo staticField in staticFields)
6565                     {
6566                         object constantValObj = staticField.GetRawConstantValue();
6567                         if (constantValObj != null)
6568                         {
6569                             long hexValue;
6570                             if (constantValObj is int)
6571                                 hexValue = ((int)constantValObj);
6572                             else if (constantValObj is long)
6573                                 hexValue = ((long)constantValObj);
6574                             else
6575                                 continue;
6576 
6577                             // ETW requires all bitmap values to be powers of 2.  Skip the ones that are not.
6578                             // TODO: Warn people about the dropping of values.
6579                             if (isbitmap && ((hexValue & (hexValue - 1)) != 0 || hexValue == 0))
6580                                 continue;
6581 
6582                             sb.Append("   <map value=\"0x").Append(hexValue.ToString("x", CultureInfo.InvariantCulture)).Append("\"");
6583                             WriteMessageAttrib(sb, "map", enumType.Name + "." + staticField.Name, staticField.Name);
6584                             sb.Append("/>").AppendLine();
6585                         }
6586                     }
6587                     sb.Append("  </").Append(mapKind).Append(">").AppendLine();
6588                 }
6589                 sb.Append(" </maps>").AppendLine();
6590             }
6591 
6592             // Write out the opcodes
6593             sb.Append(" <opcodes>").AppendLine();
6594             var sortedOpcodes = new List<int>(opcodeTab.Keys);
6595             sortedOpcodes.Sort();
6596             foreach (int opcode in sortedOpcodes)
6597             {
6598                 sb.Append("  <opcode");
6599                 WriteNameAndMessageAttribs(sb, "opcode", opcodeTab[opcode]);
6600                 sb.Append(" value=\"").Append(opcode).Append("\"/>").AppendLine();
6601             }
6602             sb.Append(" </opcodes>").AppendLine();
6603 
6604             // Write out the keywords
6605             if (keywordTab != null)
6606             {
6607                 sb.Append(" <keywords>").AppendLine();
6608                 var sortedKeywords = new List<ulong>(keywordTab.Keys);
6609                 sortedKeywords.Sort();
6610                 foreach (ulong keyword in sortedKeywords)
6611                 {
6612                     sb.Append("  <keyword");
6613                     WriteNameAndMessageAttribs(sb, "keyword", keywordTab[keyword]);
6614                     sb.Append(" mask=\"0x").Append(keyword.ToString("x", CultureInfo.InvariantCulture)).Append("\"/>").AppendLine();
6615                 }
6616                 sb.Append(" </keywords>").AppendLine();
6617             }
6618 
6619             sb.Append(" <events>").AppendLine();
6620             sb.Append(events);
6621             sb.Append(" </events>").AppendLine();
6622 
6623             sb.Append(" <templates>").AppendLine();
6624             if (templates.Length > 0)
6625             {
6626                 sb.Append(templates);
6627             }
6628             else
6629             {
6630                 // Work around a cornercase ETW issue where a manifest with no templates causes
6631                 // ETW events to not get sent to their associated channel.
6632                 sb.Append("    <template tid=\"_empty\"></template>").AppendLine();
6633             }
6634             sb.Append(" </templates>").AppendLine();
6635 
6636             sb.Append("</provider>").AppendLine();
6637             sb.Append("</events>").AppendLine();
6638             sb.Append("</instrumentation>").AppendLine();
6639 
6640             // Output the localization information.
6641             sb.Append("<localization>").AppendLine();
6642 
6643             List<CultureInfo> cultures = null;
6644             if (resources != null && (flags & EventManifestOptions.AllCultures) != 0)
6645             {
6646                 cultures = GetSupportedCultures(resources);
6647             }
6648             else
6649             {
6650                 cultures = new List<CultureInfo>();
6651                 cultures.Add(CultureInfo.CurrentUICulture);
6652             }
6653 #if ES_BUILD_STANDALONE || ES_BUILD_PN
6654             var sortedStrings = new List<string>(stringTab.Keys);
6655             sortedStrings.Sort();
6656 #else
6657             // DD 947936
6658             var sortedStrings = new string[stringTab.Keys.Count];
6659             stringTab.Keys.CopyTo(sortedStrings, 0);
6660             // Avoid using public Array.Sort as that attempts to access BinaryCompatibility. Unfortunately FrameworkEventSource gets called
6661             // very early in the app domain creation, when _FusionStore is not set up yet, resulting in a failure to run the static constructory
6662             // for BinaryCompatibility. This failure is then cached and a TypeInitializationException is thrown every time some code attampts to
6663             // access BinaryCompatibility.
6664             ArraySortHelper<string>.IntrospectiveSort(sortedStrings, 0, sortedStrings.Length, string.Compare);
6665 #endif
6666             foreach (var ci in cultures)
6667             {
6668                 sb.Append(" <resources culture=\"").Append(ci.Name).Append("\">").AppendLine();
6669                 sb.Append("  <stringTable>").AppendLine();
6670 
6671                 foreach (var stringKey in sortedStrings)
6672                 {
6673                     string val = GetLocalizedMessage(stringKey, ci, etwFormat: true);
6674                     sb.Append("   <string id=\"").Append(stringKey).Append("\" value=\"").Append(val).Append("\"/>").AppendLine();
6675                 }
6676                 sb.Append("  </stringTable>").AppendLine();
6677                 sb.Append(" </resources>").AppendLine();
6678             }
6679             sb.Append("</localization>").AppendLine();
6680             sb.AppendLine("</instrumentationManifest>");
6681             return sb.ToString();
6682         }
6683 
6684 #region private
WriteNameAndMessageAttribs(StringBuilder stringBuilder, string elementName, string name)6685         private void WriteNameAndMessageAttribs(StringBuilder stringBuilder, string elementName, string name)
6686         {
6687             stringBuilder.Append(" name=\"").Append(name).Append("\"");
6688             WriteMessageAttrib(sb, elementName, name, name);
6689         }
WriteMessageAttrib(StringBuilder stringBuilder, string elementName, string name, string value)6690         private void WriteMessageAttrib(StringBuilder stringBuilder, string elementName, string name, string value)
6691         {
6692             string key = elementName + "_" + name;
6693             // See if the user wants things localized.
6694             if (resources != null)
6695             {
6696                 // resource fallback: strings in the neutral culture will take precedence over inline strings
6697                 string localizedString = resources.GetString(key, CultureInfo.InvariantCulture);
6698                 if (localizedString != null)
6699                     value = localizedString;
6700             }
6701             if (value == null)
6702                 return;
6703 
6704             stringBuilder.Append(" message=\"$(string.").Append(key).Append(")\"");
6705             string prevValue;
6706             if (stringTab.TryGetValue(key, out prevValue) && !prevValue.Equals(value))
6707             {
6708                 ManifestError(SR.Format(SR.EventSource_DuplicateStringKey, key), true);
6709                 return;
6710             }
6711 
6712             stringTab[key] = value;
6713         }
GetLocalizedMessage(string key, CultureInfo ci, bool etwFormat)6714         internal string GetLocalizedMessage(string key, CultureInfo ci, bool etwFormat)
6715         {
6716             string value = null;
6717             if (resources != null)
6718             {
6719                 string localizedString = resources.GetString(key, ci);
6720                 if (localizedString != null)
6721                 {
6722                     value = localizedString;
6723                     if (etwFormat && key.StartsWith("event_", StringComparison.Ordinal))
6724                     {
6725                         var evtName = key.Substring("event_".Length);
6726                         value = TranslateToManifestConvention(value, evtName);
6727                     }
6728                 }
6729             }
6730             if (etwFormat && value == null)
6731                 stringTab.TryGetValue(key, out value);
6732 
6733             return value;
6734         }
6735 
6736         /// <summary>
6737         /// There's no API to enumerate all languages an assembly is localized into, so instead
6738         /// we enumerate through all the "known" cultures and attempt to load a corresponding satellite
6739         /// assembly
6740         /// </summary>
6741         /// <param name="resources"></param>
6742         /// <returns></returns>
GetSupportedCultures(ResourceManager resources)6743         private static List<CultureInfo> GetSupportedCultures(ResourceManager resources)
6744         {
6745             var cultures = new List<CultureInfo>();
6746 
6747             if (!cultures.Contains(CultureInfo.CurrentUICulture))
6748                 cultures.Insert(0, CultureInfo.CurrentUICulture);
6749             return cultures;
6750         }
6751 
GetLevelName(EventLevel level)6752         private static string GetLevelName(EventLevel level)
6753         {
6754             return (((int)level >= 16) ? "" : "win:") + level.ToString();
6755         }
6756 
6757 #if FEATURE_MANAGED_ETW_CHANNELS
GetChannelName(EventChannel channel, string eventName, string eventMessage)6758         private string GetChannelName(EventChannel channel, string eventName, string eventMessage)
6759         {
6760             ChannelInfo info = null;
6761             if (channelTab == null || !channelTab.TryGetValue((int)channel, out info))
6762             {
6763                 if (channel < EventChannel.Admin) // || channel > EventChannel.Debug)
6764                     ManifestError(SR.Format(SR.EventSource_UndefinedChannel, channel, eventName));
6765 
6766                 // allow channels to be auto-defined.  The well known ones get their well known names, and the
6767                 // rest get names Channel<N>.  This allows users to modify the Manifest if they want more advanced features.
6768                 if (channelTab == null)
6769                     channelTab = new Dictionary<int, ChannelInfo>(4);
6770 
6771                 string channelName = channel.ToString();        // For well know channels this is a nice name, otherwise a number
6772                 if (EventChannel.Debug < channel)
6773                     channelName = "Channel" + channelName;      // Add a 'Channel' prefix for numbers.
6774 
6775                 AddChannel(channelName, (int)channel, GetDefaultChannelAttribute(channel));
6776                 if (!channelTab.TryGetValue((int)channel, out info))
6777                     ManifestError(SR.Format(SR.EventSource_UndefinedChannel, channel, eventName));
6778             }
6779             // events that specify admin channels *must* have non-null "Message" attributes
6780             if (resources != null && eventMessage == null)
6781                 eventMessage = resources.GetString("event_" + eventName, CultureInfo.InvariantCulture);
6782             if (info.Attribs.EventChannelType == EventChannelType.Admin && eventMessage == null)
6783                 ManifestError(SR.Format(SR.EventSource_EventWithAdminChannelMustHaveMessage, eventName, info.Name));
6784             return info.Name;
6785         }
6786 #endif
GetTaskName(EventTask task, string eventName)6787         private string GetTaskName(EventTask task, string eventName)
6788         {
6789             if (task == EventTask.None)
6790                 return "";
6791 
6792             string ret;
6793             if (taskTab == null)
6794                 taskTab = new Dictionary<int, string>();
6795             if (!taskTab.TryGetValue((int)task, out ret))
6796                 ret = taskTab[(int)task] = eventName;
6797             return ret;
6798         }
6799 
GetOpcodeName(EventOpcode opcode, string eventName)6800         private string GetOpcodeName(EventOpcode opcode, string eventName)
6801         {
6802             switch (opcode)
6803             {
6804                 case EventOpcode.Info:
6805                     return "win:Info";
6806                 case EventOpcode.Start:
6807                     return "win:Start";
6808                 case EventOpcode.Stop:
6809                     return "win:Stop";
6810                 case EventOpcode.DataCollectionStart:
6811                     return "win:DC_Start";
6812                 case EventOpcode.DataCollectionStop:
6813                     return "win:DC_Stop";
6814                 case EventOpcode.Extension:
6815                     return "win:Extension";
6816                 case EventOpcode.Reply:
6817                     return "win:Reply";
6818                 case EventOpcode.Resume:
6819                     return "win:Resume";
6820                 case EventOpcode.Suspend:
6821                     return "win:Suspend";
6822                 case EventOpcode.Send:
6823                     return "win:Send";
6824                 case EventOpcode.Receive:
6825                     return "win:Receive";
6826             }
6827 
6828             string ret;
6829             if (opcodeTab == null || !opcodeTab.TryGetValue((int)opcode, out ret))
6830             {
6831                 ManifestError(SR.Format(SR.EventSource_UndefinedOpcode, opcode, eventName), true);
6832                 ret = null;
6833             }
6834             return ret;
6835         }
6836 
GetKeywords(ulong keywords, string eventName)6837         private string GetKeywords(ulong keywords, string eventName)
6838         {
6839 #if FEATURE_MANAGED_ETW_CHANNELS
6840             // ignore keywords associate with channels
6841             // See ValidPredefinedChannelKeywords def for more.
6842             keywords &= ~ValidPredefinedChannelKeywords;
6843 #endif
6844 
6845             string ret = "";
6846             for (ulong bit = 1; bit != 0; bit <<= 1)
6847             {
6848                 if ((keywords & bit) != 0)
6849                 {
6850                     string keyword = null;
6851                     if ((keywordTab == null || !keywordTab.TryGetValue(bit, out keyword)) &&
6852                         (bit >= (ulong)0x1000000000000))
6853                     {
6854                         // do not report Windows reserved keywords in the manifest (this allows the code
6855                         // to be resilient to potential renaming of these keywords)
6856                         keyword = string.Empty;
6857                     }
6858                     if (keyword == null)
6859                     {
6860                         ManifestError(SR.Format(SR.EventSource_UndefinedKeyword, "0x" + bit.ToString("x", CultureInfo.CurrentCulture), eventName), true);
6861                         keyword = string.Empty;
6862                     }
6863                     if (ret.Length != 0 && keyword.Length != 0)
6864                         ret = ret + " ";
6865                     ret = ret + keyword;
6866                 }
6867             }
6868             return ret;
6869         }
6870 
GetTypeName(Type type)6871         private string GetTypeName(Type type)
6872         {
6873             if (type.IsEnum())
6874             {
6875                 FieldInfo[] fields = type.GetFields(BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance);
6876                 var typeName = GetTypeName(fields[0].FieldType);
6877                 return typeName.Replace("win:Int", "win:UInt"); // ETW requires enums to be unsigned.
6878             }
6879 
6880             return GetTypeNameHelper(type);
6881         }
6882 
UpdateStringBuilder(ref StringBuilder stringBuilder, string eventMessage, int startIndex, int count)6883         private static void UpdateStringBuilder(ref StringBuilder stringBuilder, string eventMessage, int startIndex, int count)
6884         {
6885             if (stringBuilder == null)
6886                 stringBuilder = new StringBuilder();
6887             stringBuilder.Append(eventMessage, startIndex, count);
6888         }
6889 
6890         private static readonly string[] s_escapes = { "&amp;", "&lt;", "&gt;", "&apos;", "&quot;", "%r", "%n", "%t" };
6891         // Manifest messages use %N conventions for their message substitutions.   Translate from
6892         // .NET conventions.   We can't use RegEx for this (we are in mscorlib), so we do it 'by hand'
TranslateToManifestConvention(string eventMessage, string evtName)6893         private string TranslateToManifestConvention(string eventMessage, string evtName)
6894         {
6895             StringBuilder stringBuilder = null;        // We lazily create this
6896             int writtenSoFar = 0;
6897             int chIdx = -1;
6898             for (int i = 0; ;)
6899             {
6900                 if (i >= eventMessage.Length)
6901                 {
6902                     if (stringBuilder == null)
6903                         return eventMessage;
6904                     UpdateStringBuilder(ref stringBuilder, eventMessage, writtenSoFar, i - writtenSoFar);
6905                     return stringBuilder.ToString();
6906                 }
6907 
6908                 if (eventMessage[i] == '%')
6909                 {
6910                     // handle format message escaping character '%' by escaping it
6911                     UpdateStringBuilder(ref stringBuilder, eventMessage, writtenSoFar, i - writtenSoFar);
6912                     stringBuilder.Append("%%");
6913                     i++;
6914                     writtenSoFar = i;
6915                 }
6916                 else if (i < eventMessage.Length - 1 &&
6917                     (eventMessage[i] == '{' && eventMessage[i + 1] == '{' || eventMessage[i] == '}' && eventMessage[i + 1] == '}'))
6918                 {
6919                     // handle C# escaped '{" and '}'
6920                     UpdateStringBuilder(ref stringBuilder, eventMessage, writtenSoFar, i - writtenSoFar);
6921                     stringBuilder.Append(eventMessage[i]);
6922                     i++; i++;
6923                     writtenSoFar = i;
6924                 }
6925                 else if (eventMessage[i] == '{')
6926                 {
6927                     int leftBracket = i;
6928                     i++;
6929                     int argNum = 0;
6930                     while (i < eventMessage.Length && Char.IsDigit(eventMessage[i]))
6931                     {
6932                         argNum = argNum * 10 + eventMessage[i] - '0';
6933                         i++;
6934                     }
6935                     if (i < eventMessage.Length && eventMessage[i] == '}')
6936                     {
6937                         i++;
6938                         UpdateStringBuilder(ref stringBuilder, eventMessage, writtenSoFar, leftBracket - writtenSoFar);
6939                         int manIndex = TranslateIndexToManifestConvention(argNum, evtName);
6940                         stringBuilder.Append('%').Append(manIndex);
6941                         // An '!' after the insert specifier {n} will be interpreted as a literal.
6942                         // We'll escape it so that mc.exe does not attempt to consider it the
6943                         // beginning of a format string.
6944                         if (i < eventMessage.Length && eventMessage[i] == '!')
6945                         {
6946                             i++;
6947                             stringBuilder.Append("%!");
6948                         }
6949                         writtenSoFar = i;
6950                     }
6951                     else
6952                     {
6953                         ManifestError(SR.Format(SR.EventSource_UnsupportedMessageProperty, evtName, eventMessage));
6954                     }
6955                 }
6956                 else if ((chIdx = "&<>'\"\r\n\t".IndexOf(eventMessage[i])) >= 0)
6957                 {
6958                     UpdateStringBuilder(ref stringBuilder, eventMessage, writtenSoFar, i - writtenSoFar);
6959                     i++;
6960                     stringBuilder.Append(s_escapes[chIdx]);
6961                     writtenSoFar = i;
6962                 }
6963                 else
6964                     i++;
6965             }
6966         }
6967 
TranslateIndexToManifestConvention(int idx, string evtName)6968         private int TranslateIndexToManifestConvention(int idx, string evtName)
6969         {
6970             List<int> byteArrArgIndices;
6971             if (perEventByteArrayArgIndices.TryGetValue(evtName, out byteArrArgIndices))
6972             {
6973                 foreach (var byArrIdx in byteArrArgIndices)
6974                 {
6975                     if (idx >= byArrIdx)
6976                         ++idx;
6977                     else
6978                         break;
6979                 }
6980             }
6981             return idx + 1;
6982         }
6983 
6984 #if FEATURE_MANAGED_ETW_CHANNELS
6985         class ChannelInfo
6986         {
6987             public string Name;
6988             public ulong Keywords;
6989             public EventChannelAttribute Attribs;
6990         }
6991 #endif
6992 
6993         Dictionary<int, string> opcodeTab;
6994         Dictionary<int, string> taskTab;
6995 #if FEATURE_MANAGED_ETW_CHANNELS
6996         Dictionary<int, ChannelInfo> channelTab;
6997 #endif
6998         Dictionary<ulong, string> keywordTab;
6999         Dictionary<string, Type> mapsTab;
7000 
7001         Dictionary<string, string> stringTab;       // Maps unlocalized strings to localized ones
7002 
7003 #if FEATURE_MANAGED_ETW_CHANNELS
7004         // WCF used EventSource to mimic a existing ETW manifest.   To support this
7005         // in just their case, we allowed them to specify the keywords associated
7006         // with their channels explicitly.   ValidPredefinedChannelKeywords is
7007         // this set of channel keywords that we allow to be explicitly set.  You
7008         // can ignore these bits otherwise.
7009         internal const ulong ValidPredefinedChannelKeywords = 0xF000000000000000;
7010         ulong nextChannelKeywordBit = 0x8000000000000000;   // available Keyword bit to be used for next channel definition, grows down
7011         const int MaxCountChannels = 8; // a manifest can defined at most 8 ETW channels
7012 #endif
7013 
7014         StringBuilder sb;               // Holds the provider information.
7015         StringBuilder events;           // Holds the events.
7016         StringBuilder templates;
7017 
7018 #if FEATURE_MANAGED_ETW_CHANNELS
7019         string providerName;
7020 #endif
7021         ResourceManager resources;      // Look up localized strings here.
7022         EventManifestOptions flags;
7023         IList<string> errors;           // list of currently encountered errors
7024         Dictionary<string, List<int>> perEventByteArrayArgIndices;  // "event_name" -> List_of_Indices_of_Byte[]_Arg
7025 
7026         // State we track between StartEvent and EndEvent.
7027         string eventName;               // Name of the event currently being processed.
7028         int numParams;                  // keeps track of the number of args the event has.
7029         List<int> byteArrArgIndices;    // keeps track of the index of each byte[] argument
7030 #endregion
7031     }
7032 
7033     /// <summary>
7034     /// Used to send the m_rawManifest into the event dispatcher as a series of events.
7035     /// </summary>
7036     internal struct ManifestEnvelope
7037     {
7038         public const int MaxChunkSize = 0xFF00;
7039         public enum ManifestFormats : byte
7040         {
7041             SimpleXmlFormat = 1,          // simply dump the XML manifest as UTF8
7042         }
7043 
7044 #if FEATURE_MANAGED_ETW
7045         public ManifestFormats Format;
7046         public byte MajorVersion;
7047         public byte MinorVersion;
7048         public byte Magic;
7049         public ushort TotalChunks;
7050         public ushort ChunkNumber;
7051 #endif
7052     };
7053 
7054 #endregion
7055 }
7056 
7057