1 // Copyright (c) Microsoft. All rights reserved.
2 // Licensed under the MIT license. See LICENSE file in the project root for full license information.
4 // This program uses code hyperlinks available as part of the HyperAddin Visual Studio plug-in.
5 // It is available from http://www.codeplex.com/hyperAddin
8 #define FEATURE_MANAGED_ETW
10 #if !ES_BUILD_STANDALONE && !CORECLR && !PROJECTN
11 #define FEATURE_ACTIVITYSAMPLING
12 #endif // !ES_BUILD_STANDALONE
14 #endif // !PLATFORM_UNIX
16 #if ES_BUILD_STANDALONE
17 #define FEATURE_MANAGED_ETW_CHANNELS
18 // #define FEATURE_ADVANCED_MANAGED_ETW_CHANNELS
21 /* DESIGN NOTES DESIGN NOTES DESIGN NOTES DESIGN NOTES */
23 // Over the years EventSource has become more complex and so it is important to understand
24 // the basic structure of the code to insure that it does not grow more complex.
28 // PRINCIPLE: EventSource - ETW decoupling
30 // Conceptually and EventSouce is something takes event logging data from the source methods
31 // To the EventListener that can subscribe them. Note that CONCEPTUALLY EVENTSOURCES DON'T
32 // KNOW ABOUT ETW!. The MODEL of the system is that there is a special EventListern Which
33 // we will call the EtwEventListener, that forwards commands from ETW to EventSources and
34 // listeners to the EventSources and forwards on those events to ETW. THus the model should
35 // be that you DON'T NEED ETW.
37 // Now in actual practice, EventSouce have rather intimate knowledge of ETW and send events
38 // to it directly, but this can be VIEWED AS AN OPTIMIATION.
40 // Basic Event Data Flow:
42 // There are two ways for event Data to enter the system
43 // 1) WriteEvent* and friends. This is called the 'contract' based approach because
44 // you write a method per event which forms a contract that is know at compile time.
45 // In this scheme each event is given an EVENTID (small integer). which is its identity
46 // 2) Write<T> methods. This is called the 'dynamic' approach because new events
47 // can be created on the fly. Event identity is determined by the event NAME, and these
48 // are not quite as efficient at runtime since you have at least a hash table lookup
49 // on every event write.
51 // EventSource-EventListener transfer fully support both ways of writing events (either contract
52 // based (WriteEvent*) or dynamic (Write<T>). Both way fully support the same set of data
53 // types. It is suggested, however, that you use the contract based approach when the event scheme
54 // is known at compile time (that is whenever possible). It is more efficient, but more importantly
55 // it makes the contract very explicit, and centralizes all policy about logging. These are good
56 // things. The Write<T> API is really meant for more ad-hoc
60 // Note that EventSource-EventListeners have a conceptual serialization-deserialization that happens
61 // during the transfer. In particular object identity is not preserved, some objects are morphed,
62 // and not all data types are supported. In particular you can pass
64 // A Valid type to log to an EventSource include
65 // * Primitive data types
66 // * IEnumerable<T> of valid types T (this include arrays) (* New for V4.6)
67 // * Explicitly Opted in class or struct with public property Getters over Valid types. (* New for V4.6)
69 // This set of types is roughly a generalization of JSON support (Basically primitives, bags, and arrays).
71 // Explicitly allowed structs include (* New for V4.6)
72 // * Marked with the EventData attribute
73 // * implicitly defined (e.g the C# new {x = 3, y = 5} syntax)
74 // * KeyValuePair<K,V> (thus dictionaries can be passed since they are an IEnumerable of KeyValuePair)
76 // When classes are returned in an EventListener, what is returned is something that implements
77 // IDictionary<string, T>. Thus when objects are passed to an EventSource they are transformed
78 // into a key-value bag (the IDictionary<string, T>) for consumption in the listener. These
79 // are obvious NOT the original objects.
81 // ETWserialization formats:
83 // As mentioned conceptually EventSource's send data to EventListeners and there is a conceptual
84 // copy/morph of that data as described above. In addition the .NET framework supports a conceptual
85 // ETWListener that will send the data to then ETW stream. If you use this feature, the data needs
86 // to be serialized in a way that ETW supports. ETW supports the following serialization formats
88 // 1) Manifest Based serialization.
89 // 2) SelfDescribing serialization (TraceLogging style in the TraceLogging directory)
91 // A key factor is that the Write<T> method, which support on the fly definition of events, can't
92 // support the manifest based serialization because the manifest needs the schema of all events
93 // to be known before any events are emitted. This implies the following
95 // If you use Write<T> and the output goes to ETW it will use the SelfDescribing format.
96 // If you use the EventSource(string) constructor for an eventSource (in which you don't
97 // create a subclass), the default is also to use Self-Describing serialization. In addition
98 // you can use the EventSoruce(EventSourceSettings) constructor to also explicitly specify
99 // Self-Describing serialization format. These effect the WriteEvent* APIs going to ETW.
101 // Note that none of this ETW serialization logic affects EventListeners. Only the ETW listener.
103 // *************************************************************************************
104 // *** INTERNALS: Event Propagation
106 // Data enters the system either though
108 // 1) A user defined method in the user defined subclass of EventSource which calls
109 // A) A typesafe type specific overload of WriteEvent(ID, ...) e.g. WriteEvent(ID, string, string)
110 // * which calls into the unsafe WriteEventCore(ID COUNT EventData*) WriteEventWithRelatedActivityIdCore()
111 // B) The typesafe overload WriteEvent(ID, object[]) which calls the private helper WriteEventVarargs(ID, Guid* object[])
112 // C) Directly into the unsafe WriteEventCore(ID, COUNT EventData*) or WriteEventWithRelatedActivityIdCore()
114 // All event data eventually flows to one of
115 // * WriteEventWithRelatedActivityIdCore(ID, Guid*, COUNT, EventData*)
116 // * WriteEventVarargs(ID, Guid*, object[])
118 // 2) A call to one of the overloads of Write<T>. All these overloads end up in
119 // * WriteImpl<T>(EventName, Options, Data, Guid*, Guid*)
121 // On output there are the following routines
122 // Writing to all listeners that are NOT ETW, we have the following routines
123 // * WriteToAllListeners(ID, Guid*, COUNT, EventData*)
124 // * WriteToAllListeners(ID, Guid*, object[])
125 // * WriteToAllListeners(NAME, Guid*, EventPayload)
127 // EventPayload is the internal type that implements the IDictionary<string, object> interface
128 // The EventListeners will pass back for serialized classes for nested object, but
129 // WriteToAllListeners(NAME, Guid*, EventPayload) unpacks this uses the fields as if they
130 // were parameters to a method.
132 // The first two are used for the WriteEvent* case, and the later is used for the Write<T> case.
134 // Writing to ETW, Manifest Based
135 // EventProvider.WriteEvent(EventDescriptor, Guid*, COUNT, EventData*)
136 // EventProvider.WriteEvent(EventDescriptor, Guid*, object[])
137 // Writing to ETW, Self-Describing format
138 // WriteMultiMerge(NAME, Options, Types, EventData*)
139 // WriteMultiMerge(NAME, Options, Types, object[])
140 // WriteImpl<T> has logic that knows how to serialize (like WriteMultiMerge) but also knows
143 // All ETW writes eventually call
144 // EventWriteTransfer (native PINVOKE wrapper)
145 // EventWriteTransferWrapper (fixes compat problem if you pass null as the related activityID)
146 // EventProvider.WriteEventRaw - sets last error
147 // EventSource.WriteEventRaw - Does EventSource exception handling logic
150 // EventProvider.WriteEvent(EventDescriptor, Guid*, COUNT, EventData*)
151 // EventProvider.WriteEvent(EventDescriptor, Guid*, object[])
153 // Serialization: We have a bit of a hodge-podge of serializers right now. Only the one for ETW knows
154 // how to deal with nested classes or arrays. I will call this serializer the 'TypeInfo' serializer
155 // since it is the TraceLoggingTypeInfo structure that knows how to do this. Effectively for a type you
156 // can call one of these
157 // WriteMetadata - transforms the type T into serialization meta data blob for that type
158 // WriteObjectData - transforms an object of T into serialization meta data blob for that type
159 // GetData - transforms an object of T into its deserialized form suitable for passing to EventListener.
160 // The first two are used to serialize something for ETW. The second one is used to transform the object
161 // for use by the EventListener. We also have a 'DecodeObject' method that will take a EventData* and
162 // deserialize to pass to an EventListener, but it only works on primitive types (types supported in version V4.5).
164 // It is an important observation that while EventSource does support users directly calling with EventData*
165 // blobs, we ONLY support that for the primitive types (V4.5 level support). Thus while there is a EventData*
166 // path through the system it is only for some types. The object[] path is the more general (but less efficient) path.
168 // TODO There is cleanup needed There should be no divergence until WriteEventRaw.
170 // TODO: We should have a single choke point (right now we always have this parallel EventData* and object[] path. This
171 // was historical (at one point we tried to pass object directly from EventSoruce to EventListener. That was always
172 // fragile and a compatibility headache, but we have finally been forced into the idea that there is always a transformation.
173 // This allows us to use the EventData* form to be the canonical data format in the low level APIs. This also gives us the
174 // opportunity to expose this format to EventListeners in the future.
177 using System.Runtime.CompilerServices;
178 #if FEATURE_ACTIVITYSAMPLING
179 using System.Collections.Concurrent;
181 using System.Collections.Generic;
182 using System.Collections.ObjectModel;
183 using System.Diagnostics;
184 using System.Diagnostics.CodeAnalysis;
185 using System.Globalization;
186 using System.Reflection;
187 using System.Resources;
188 using System.Security;
189 using System.Security.Permissions;
191 using System.Threading;
192 using Microsoft.Win32;
194 #if ES_BUILD_STANDALONE
195 using Environment = Microsoft.Diagnostics.Tracing.Internal.Environment;
196 using EventDescriptor = Microsoft.Diagnostics.Tracing.EventDescriptor;
198 using System.Threading.Tasks;
199 using EventDescriptor = System.Diagnostics.Tracing.EventDescriptor;
202 using Microsoft.Reflection;
204 #if !ES_BUILD_AGAINST_DOTNET_V35
205 using Contract = System.Diagnostics.Contracts.Contract;
207 using Contract = Microsoft.Diagnostics.Contracts.Internal.Contract;
210 #if ES_BUILD_STANDALONE
211 namespace Microsoft.Diagnostics.Tracing
213 namespace System.Diagnostics.Tracing
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.
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.
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"/>).
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.
239 /// This is a minimal definition for a custom event source:
241 /// [EventSource(Name="Samples-Demos-Minimal")]
242 /// sealed class MinimalEventSource : EventSource
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() {}
251 public partial class EventSource : IDisposable
254 #if FEATURE_EVENTSOURCE_XPLAT
255 private static readonly EventListener persistent_Xplat_Listener = XplatEventLogger.InitializePersistentListener();
256 #endif //FEATURE_EVENTSOURCE_XPLAT
259 /// The human-friendly name of the eventSource. It defaults to the simple name of the class
261 public string Name { get { return m_name; } }
263 /// Every eventSource is assigned a GUID to uniquely identify it to the system.
265 public Guid Guid { get { return m_guid; } }
268 /// Returns true if the eventSource has been enabled at all. This is the prefered test
269 /// to be performed before a relatively expensive EventSource operation.
271 [SuppressMessage("Microsoft.Concurrency", "CA8001", Justification = "This does not need to be correct when racing with other threads")]
272 public bool IsEnabled()
274 return m_eventSourceEnabled;
278 /// Returns true if events with greater than or equal 'level' and have one of 'keywords' set are enabled.
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.
286 [SuppressMessage("Microsoft.Concurrency", "CA8001", Justification = "This does not need to be correct when racing with other threads")]
287 public bool IsEnabled(EventLevel level, EventKeywords keywords)
289 return IsEnabled(level, keywords, EventChannel.None);
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.
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.
302 [SuppressMessage("Microsoft.Concurrency", "CA8001", Justification = "This does not need to be correct when racing with other threads")]
303 public bool IsEnabled(EventLevel level, EventKeywords keywords, EventChannel channel)
305 if (!m_eventSourceEnabled)
308 if (!IsEnabledCommon(m_eventSourceEnabled, m_level, m_matchAnyKeyword, level, keywords, channel))
311 #if !FEATURE_ACTIVITYSAMPLING
315 #else // FEATURE_ACTIVITYSAMPLING
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 //================================================================================
327 // At this point we believe the event is enabled, however we now need to check
328 // if we filter because of activity
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)
336 // if there's at least one legacy ETW listener we can't filter this
337 if (m_legacySessions != null && m_legacySessions.Count > 0)
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)
345 // See if all listeners have activity filters that would block the event.
346 for (int perEventSourceSessionId = 0; perEventSourceSessionId < SessionMask.MAX; ++perEventSourceSessionId)
348 EtwSession etwSession = m_etwSessionIdMap[perEventSourceSessionId];
349 if (etwSession == null)
352 ActivityFilter activityFilter = etwSession.m_activityFilter;
353 if (activityFilter == null ||
354 ActivityFilter.GetFilter(activityFilter, this) == null)
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)
361 else if (ActivityFilter.IsCurrentActivityActive(activityFilter))
365 // for regular event listeners
366 var curDispatcher = m_Dispatchers;
367 while (curDispatcher != null)
369 ActivityFilter activityFilter = curDispatcher.m_Listener.m_activityFilter;
370 if (activityFilter == null)
372 // See if any event is enabled.
373 for (int i = 0; i < curDispatcher.m_EventEnabled.Length; i++)
374 if (curDispatcher.m_EventEnabled[i])
377 else if (ActivityFilter.IsCurrentActivityActive(activityFilter))
379 curDispatcher = curDispatcher.m_Next;
382 // Every listener has an activity filter that is blocking writing the event,
383 // thus the event is not enabled.
385 #endif // OPTIMIZE_IS_ENABLED
387 #endif // FEATURE_ACTIVITYSAMPLING
391 /// Returns the settings for the event source instance
393 public EventSourceSettings Settings
395 get { return m_config; }
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.
404 public static Guid GetGuid(Type eventSourceType)
406 if (eventSourceType == null)
407 throw new ArgumentNullException("eventSourceType");
408 Contract.EndContractBlock();
410 EventSourceAttribute attrib = (EventSourceAttribute)GetCustomAttributeHelper(eventSourceType, typeof(EventSourceAttribute));
411 string name = eventSourceType.Name;
414 if (attrib.Guid != null)
417 #if !ES_BUILD_AGAINST_DOTNET_V35
418 if (Guid.TryParse(attrib.Guid, out g))
421 try { return new Guid(attrib.Guid); }
422 catch (Exception) { }
426 if (attrib.Name != null)
431 throw new ArgumentException(Environment.GetResourceString("Argument_InvalidTypeName"), "eventSourceType");
433 return GenerateGuidFromName(name.ToUpperInvariant()); // Make it case insensitive.
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.
440 public static string GetName(Type eventSourceType)
442 return GetName(eventSourceType, EventManifestOptions.None);
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.
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>
455 public static string GenerateManifest(Type eventSourceType, string assemblyPathToIncludeInManifest)
457 return GenerateManifest(eventSourceType, assemblyPathToIncludeInManifest, EventManifestOptions.None);
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.
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>
471 public static string GenerateManifest(Type eventSourceType, string assemblyPathToIncludeInManifest, EventManifestOptions flags)
473 if (eventSourceType == null)
474 throw new ArgumentNullException("eventSourceType");
475 Contract.EndContractBlock();
477 byte[] manifestBytes = EventSource.CreateManifestAndDescriptors(eventSourceType, assemblyPathToIncludeInManifest, null, flags);
478 return (manifestBytes == null) ? null : Encoding.UTF8.GetString(manifestBytes, 0, manifestBytes.Length);
481 // EventListener support
483 /// returns a list (IEnumerable) of all sources in the appdomain). EventListeners typically need this.
485 /// <returns></returns>
486 public static IEnumerable<EventSource> GetSources()
488 var ret = new List<EventSource>();
489 lock (EventListener.EventListenersLock)
491 foreach (WeakReference eventSourceRef in EventListener.s_EventSources)
493 EventSource eventSource = eventSourceRef.Target as EventSource;
494 if (eventSource != null && !eventSource.IsDisposed)
495 ret.Add(eventSource);
502 /// Send a command to a particular EventSource identified by 'eventSource'.
503 /// Calling this routine simply forwards the command to the EventSource.OnEventCommand
504 /// callback. What the EventSource does with the command and its arguments are from
505 /// that point EventSource-specific.
507 /// <param name="eventSource">The instance of EventSource to send the command to</param>
508 /// <param name="command">A positive user-defined EventCommand, or EventCommand.SendManifest</param>
509 /// <param name="commandArguments">A set of (name-argument, value-argument) pairs associated with the command</param>
510 public static void SendCommand(EventSource eventSource, EventCommand command, IDictionary<string, string> commandArguments)
512 if (eventSource == null)
513 throw new ArgumentNullException("eventSource");
515 // User-defined EventCommands should not conflict with the reserved commands.
516 if ((int)command <= (int)EventCommand.Update && (int)command != (int)EventCommand.SendManifest)
517 throw new ArgumentException(Environment.GetResourceString("EventSource_InvalidCommand"), "command");
519 eventSource.SendCommand(null, 0, 0, command, true, EventLevel.LogAlways, EventKeywords.None, commandArguments);
522 // ActivityID support (see also WriteEventWithRelatedActivityIdCore)
524 /// When a thread starts work that is on behalf of 'something else' (typically another
525 /// thread or network request) it should mark the thread as working on that other work.
526 /// This API marks the current thread as working on activity 'activityID'. This API
527 /// should be used when the caller knows the thread's current activity (the one being
528 /// overwritten) has completed. Otherwise, callers should prefer the overload that
529 /// return the oldActivityThatWillContinue (below).
531 /// All events created with the EventSource on this thread are also tagged with the
532 /// activity ID of the thread.
534 /// It is common, and good practice after setting the thread to an activity to log an event
535 /// with a 'start' opcode to indicate that precise time/thread where the new activity
538 /// <param name="activityId">A Guid that represents the new activity with which to mark
539 /// the current thread</param>
540 [System.Security.SecuritySafeCritical]
541 public static void SetCurrentThreadActivityId(Guid activityId)
543 #if FEATURE_MANAGED_ETW
544 #if FEATURE_ACTIVITYSAMPLING
545 Guid newId = activityId;
546 #endif // FEATURE_ACTIVITYSAMPLING
547 // We ignore errors to keep with the convention that EventSources do not throw errors.
548 // Note we can't access m_throwOnWrites because this is a static method.
549 if (UnsafeNativeMethods.ManifestEtw.EventActivityIdControl(
550 UnsafeNativeMethods.ManifestEtw.ActivityControl.EVENT_ACTIVITY_CTRL_GET_SET_ID,
551 ref activityId) == 0)
553 #if FEATURE_ACTIVITYSAMPLING
554 var activityDying = s_activityDying;
555 if (activityDying != null && newId != activityId)
557 if (activityId == Guid.Empty)
559 activityId = FallbackActivityId;
561 // OutputDebugString(string.Format("Activity dying: {0} -> {1}", activityId, newId));
562 activityDying(activityId); // This is actually the OLD activity ID.
564 #endif // FEATURE_ACTIVITYSAMPLING
566 #endif // FEATURE_MANAGED_ETW
567 if (TplEtwProvider.Log != null)
568 TplEtwProvider.Log.SetActivityId(activityId);
572 /// When a thread starts work that is on behalf of 'something else' (typically another
573 /// thread or network request) it should mark the thread as working on that other work.
574 /// This API marks the current thread as working on activity 'activityID'. It returns
575 /// whatever activity the thread was previously marked with. There is a convention that
576 /// callers can assume that callees restore this activity mark before the callee returns.
577 /// To encourage this this API returns the old activity, so that it can be restored later.
579 /// All events created with the EventSource on this thread are also tagged with the
580 /// activity ID of the thread.
582 /// It is common, and good practice after setting the thread to an activity to log an event
583 /// with a 'start' opcode to indicate that precise time/thread where the new activity
586 /// <param name="activityId">A Guid that represents the new activity with which to mark
587 /// the current thread</param>
588 /// <param name="oldActivityThatWillContinue">The Guid that represents the current activity
589 /// which will continue at some point in the future, on the current thread</param>
590 [System.Security.SecuritySafeCritical]
591 public static void SetCurrentThreadActivityId(Guid activityId, out Guid oldActivityThatWillContinue)
593 oldActivityThatWillContinue = activityId;
594 #if FEATURE_MANAGED_ETW
595 // We ignore errors to keep with the convention that EventSources do not throw errors.
596 // Note we can't access m_throwOnWrites because this is a static method.
597 UnsafeNativeMethods.ManifestEtw.EventActivityIdControl(
598 UnsafeNativeMethods.ManifestEtw.ActivityControl.EVENT_ACTIVITY_CTRL_GET_SET_ID,
599 ref oldActivityThatWillContinue);
600 #endif // FEATURE_MANAGED_ETW
602 // We don't call the activityDying callback here because the caller has declared that
604 if (TplEtwProvider.Log != null)
605 TplEtwProvider.Log.SetActivityId(activityId);
609 /// Retrieves the ETW activity ID associated with the current thread.
611 public static Guid CurrentThreadActivityId
613 [System.Security.SecuritySafeCritical]
616 // We ignore errors to keep with the convention that EventSources do not throw
617 // errors. Note we can't access m_throwOnWrites because this is a static method.
618 Guid retVal = new Guid();
619 #if FEATURE_MANAGED_ETW
620 UnsafeNativeMethods.ManifestEtw.EventActivityIdControl(
621 UnsafeNativeMethods.ManifestEtw.ActivityControl.EVENT_ACTIVITY_CTRL_GET_ID,
623 #endif // FEATURE_MANAGED_ETW
628 #if !ES_BUILD_STANDALONE
630 /// This property allows EventSource code to appropriately handle as "different"
631 /// activities started on different threads that have not had an activity created on them.
633 internal static Guid InternalCurrentThreadActivityId
635 [System.Security.SecurityCritical]
638 Guid retval = CurrentThreadActivityId;
639 if (retval == Guid.Empty)
641 retval = FallbackActivityId;
647 internal static Guid FallbackActivityId
649 [System.Security.SecurityCritical]
652 #pragma warning disable 612, 618
653 // Managed thread IDs are more aggressively re-used than native thread IDs,
654 // so we'll use the latter...
655 return new Guid(unchecked((uint)AppDomain.GetCurrentThreadId()),
656 unchecked((ushort)s_currentPid), unchecked((ushort)(s_currentPid >> 16)),
657 0x94, 0x1b, 0x87, 0xd5, 0xa6, 0x5c, 0x36, 0x64);
658 #pragma warning restore 612, 618
661 #endif // !ES_BUILD_STANDALONE
663 // Error APIs. (We don't throw by default, but you can probe for status)
667 /// 1) Logging is often optional and thus should not generate fatal errors (exceptions)
668 /// 2) EventSources are often initialized in class constructors (which propagate exceptions poorly)
670 /// The event source constructor does not throw exceptions. Instead we remember any exception that
671 /// was generated (it is also logged to Trace.WriteLine).
673 public Exception ConstructionException { get { return m_constructionException; } }
676 /// EventSources can have arbitrary string key-value pairs associated with them called Traits.
677 /// These traits are not interpreted by the EventSource but may be interpreted by EventListeners
678 /// (e.g. like the built in ETW listener). These traits are specififed at EventSource
679 /// construction time and can be retrieved by using this GetTrait API.
681 /// <param name="key">The key to look up in the set of key-value pairs passed to the EventSource constructor</param>
682 /// <returns>The value string associated iwth key. Will return null if there is no such key.</returns>
683 public string GetTrait(string key)
685 if (m_traits != null)
687 for (int i = 0; i < m_traits.Length - 1; i += 2)
689 if (m_traits[i] == key)
690 return m_traits[i + 1];
697 /// Displays the name and GUID for the eventSource for debugging purposes.
699 public override string ToString() { return Environment.GetResourceString("EventSource_ToString", Name, Guid); }
702 /// Fires when a Command (e.g. Enable) comes from a an EventListener.
704 public event EventHandler<EventCommandEventArgs> EventCommandExecuted
710 m_eventCommandExecuted += value;
713 // If we have an EventHandler<EventCommandEventArgs> attached to the EventSource before the first command arrives
714 // It should get a chance to handle the deferred commands.
715 EventCommandEventArgs deferredCommands = m_deferredCommands;
716 while (deferredCommands != null)
718 value(this, deferredCommands);
719 deferredCommands = deferredCommands.nextCommand;
726 m_eventCommandExecuted -= value;
733 /// This is the constructor that most users will use to create their eventSource. It takes
734 /// no parameters. The ETW provider name and GUID of the EventSource are determined by the EventSource
735 /// custom attribute (so you can determine these things declaratively). If the GUID for the eventSource
736 /// is not specified in the EventSourceAttribute (recommended), it is Generated by hashing the name.
737 /// If the ETW provider name of the EventSource is not given, the name of the EventSource class is used as
738 /// the ETW provider name.
740 protected EventSource()
741 : this(EventSourceSettings.EtwManifestEventFormat)
746 /// By default calling the 'WriteEvent' methods do NOT throw on errors (they silently discard the event).
747 /// This is because in most cases users assume logging is not 'precious' and do NOT wish to have logging failures
748 /// crash the program. However for those applications where logging is 'precious' and if it fails the caller
749 /// wishes to react, setting 'throwOnEventWriteErrors' will cause an exception to be thrown if WriteEvent
750 /// fails. Note the fact that EventWrite succeeds does not necessarily mean that the event reached its destination
751 /// only that operation of writing it did not fail. These EventSources will not generate self-describing ETW events.
753 /// For compatibility only use the EventSourceSettings.ThrowOnEventWriteErrors flag instead.
755 // [Obsolete("Use the EventSource(EventSourceSettings) overload")]
756 protected EventSource(bool throwOnEventWriteErrors)
757 : this(EventSourceSettings.EtwManifestEventFormat | (throwOnEventWriteErrors ? EventSourceSettings.ThrowOnEventWriteErrors : 0))
761 /// Construct an EventSource with additional non-default settings (see EventSourceSettings for more)
763 protected EventSource(EventSourceSettings settings) : this(settings, null) { }
766 /// Construct an EventSource with additional non-default settings.
768 /// Also specify a list of key-value pairs called traits (you must pass an even number of strings).
769 /// The first string is the key and the second is the value. These are not interpreted by EventSource
770 /// itself but may be interprated the listeners. Can be fetched with GetTrait(string).
772 /// <param name="settings">See EventSourceSettings for more.</param>
773 /// <param name="traits">A collection of key-value strings (must be an even number).</param>
774 protected EventSource(EventSourceSettings settings, params string[] traits)
776 m_config = ValidateSettings(settings);
777 var myType = this.GetType();
778 Initialize(GetGuid(myType), GetName(myType), traits);
782 /// This method is called when the eventSource is updated by the controller.
784 protected virtual void OnEventCommand(EventCommandEventArgs command) { }
786 #pragma warning disable 1591
787 // optimized for common signatures (no args)
788 [SecuritySafeCritical]
789 [SuppressMessage("Microsoft.Concurrency", "CA8001", Justification = "This does not need to be correct when racing with other threads")]
790 protected unsafe void WriteEvent(int eventId)
792 WriteEventCore(eventId, 0, null);
795 // optimized for common signatures (ints)
796 [SecuritySafeCritical]
797 [SuppressMessage("Microsoft.Concurrency", "CA8001", Justification = "This does not need to be correct when racing with other threads")]
798 protected unsafe void WriteEvent(int eventId, int arg1)
800 if (m_eventSourceEnabled)
802 EventSource.EventData* descrs = stackalloc EventSource.EventData[1];
803 descrs[0].DataPointer = (IntPtr)(&arg1);
805 WriteEventCore(eventId, 1, descrs);
809 [SecuritySafeCritical]
810 [SuppressMessage("Microsoft.Concurrency", "CA8001", Justification = "This does not need to be correct when racing with other threads")]
811 protected unsafe void WriteEvent(int eventId, int arg1, int arg2)
813 if (m_eventSourceEnabled)
815 EventSource.EventData* descrs = stackalloc EventSource.EventData[2];
816 descrs[0].DataPointer = (IntPtr)(&arg1);
818 descrs[1].DataPointer = (IntPtr)(&arg2);
820 WriteEventCore(eventId, 2, descrs);
824 [SecuritySafeCritical]
825 [SuppressMessage("Microsoft.Concurrency", "CA8001", Justification = "This does not need to be correct when racing with other threads")]
826 protected unsafe void WriteEvent(int eventId, int arg1, int arg2, int arg3)
828 if (m_eventSourceEnabled)
830 EventSource.EventData* descrs = stackalloc EventSource.EventData[3];
831 descrs[0].DataPointer = (IntPtr)(&arg1);
833 descrs[1].DataPointer = (IntPtr)(&arg2);
835 descrs[2].DataPointer = (IntPtr)(&arg3);
837 WriteEventCore(eventId, 3, descrs);
841 // optimized for common signatures (longs)
842 [SecuritySafeCritical]
843 [SuppressMessage("Microsoft.Concurrency", "CA8001", Justification = "This does not need to be correct when racing with other threads")]
844 protected unsafe void WriteEvent(int eventId, long arg1)
846 if (m_eventSourceEnabled)
848 EventSource.EventData* descrs = stackalloc EventSource.EventData[1];
849 descrs[0].DataPointer = (IntPtr)(&arg1);
851 WriteEventCore(eventId, 1, descrs);
855 [SecuritySafeCritical]
856 [SuppressMessage("Microsoft.Concurrency", "CA8001", Justification = "This does not need to be correct when racing with other threads")]
857 protected unsafe void WriteEvent(int eventId, long arg1, long arg2)
859 if (m_eventSourceEnabled)
861 EventSource.EventData* descrs = stackalloc EventSource.EventData[2];
862 descrs[0].DataPointer = (IntPtr)(&arg1);
864 descrs[1].DataPointer = (IntPtr)(&arg2);
866 WriteEventCore(eventId, 2, descrs);
870 [SecuritySafeCritical]
871 [SuppressMessage("Microsoft.Concurrency", "CA8001", Justification = "This does not need to be correct when racing with other threads")]
872 protected unsafe void WriteEvent(int eventId, long arg1, long arg2, long arg3)
874 if (m_eventSourceEnabled)
876 EventSource.EventData* descrs = stackalloc EventSource.EventData[3];
877 descrs[0].DataPointer = (IntPtr)(&arg1);
879 descrs[1].DataPointer = (IntPtr)(&arg2);
881 descrs[2].DataPointer = (IntPtr)(&arg3);
883 WriteEventCore(eventId, 3, descrs);
887 // optimized for common signatures (strings)
888 [SecuritySafeCritical]
889 [SuppressMessage("Microsoft.Concurrency", "CA8001", Justification = "This does not need to be correct when racing with other threads")]
890 protected unsafe void WriteEvent(int eventId, string arg1)
892 if (m_eventSourceEnabled)
894 if (arg1 == null) arg1 = "";
895 fixed (char* string1Bytes = arg1)
897 EventSource.EventData* descrs = stackalloc EventSource.EventData[1];
898 descrs[0].DataPointer = (IntPtr)string1Bytes;
899 descrs[0].Size = ((arg1.Length + 1) * 2);
900 WriteEventCore(eventId, 1, descrs);
905 [SecuritySafeCritical]
906 [SuppressMessage("Microsoft.Concurrency", "CA8001", Justification = "This does not need to be correct when racing with other threads")]
907 protected unsafe void WriteEvent(int eventId, string arg1, string arg2)
909 if (m_eventSourceEnabled)
911 if (arg1 == null) arg1 = "";
912 if (arg2 == null) arg2 = "";
913 fixed (char* string1Bytes = arg1)
914 fixed (char* string2Bytes = arg2)
916 EventSource.EventData* descrs = stackalloc EventSource.EventData[2];
917 descrs[0].DataPointer = (IntPtr)string1Bytes;
918 descrs[0].Size = ((arg1.Length + 1) * 2);
919 descrs[1].DataPointer = (IntPtr)string2Bytes;
920 descrs[1].Size = ((arg2.Length + 1) * 2);
921 WriteEventCore(eventId, 2, descrs);
926 [SecuritySafeCritical]
927 [SuppressMessage("Microsoft.Concurrency", "CA8001", Justification = "This does not need to be correct when racing with other threads")]
928 protected unsafe void WriteEvent(int eventId, string arg1, string arg2, string arg3)
930 if (m_eventSourceEnabled)
932 if (arg1 == null) arg1 = "";
933 if (arg2 == null) arg2 = "";
934 if (arg3 == null) arg3 = "";
935 fixed (char* string1Bytes = arg1)
936 fixed (char* string2Bytes = arg2)
937 fixed (char* string3Bytes = arg3)
939 EventSource.EventData* descrs = stackalloc EventSource.EventData[3];
940 descrs[0].DataPointer = (IntPtr)string1Bytes;
941 descrs[0].Size = ((arg1.Length + 1) * 2);
942 descrs[1].DataPointer = (IntPtr)string2Bytes;
943 descrs[1].Size = ((arg2.Length + 1) * 2);
944 descrs[2].DataPointer = (IntPtr)string3Bytes;
945 descrs[2].Size = ((arg3.Length + 1) * 2);
946 WriteEventCore(eventId, 3, descrs);
951 // optimized for common signatures (string and ints)
952 [SecuritySafeCritical]
953 [SuppressMessage("Microsoft.Concurrency", "CA8001", Justification = "This does not need to be correct when racing with other threads")]
954 protected unsafe void WriteEvent(int eventId, string arg1, int arg2)
956 if (m_eventSourceEnabled)
958 if (arg1 == null) arg1 = "";
959 fixed (char* string1Bytes = arg1)
961 EventSource.EventData* descrs = stackalloc EventSource.EventData[2];
962 descrs[0].DataPointer = (IntPtr)string1Bytes;
963 descrs[0].Size = ((arg1.Length + 1) * 2);
964 descrs[1].DataPointer = (IntPtr)(&arg2);
966 WriteEventCore(eventId, 2, descrs);
971 [SecuritySafeCritical]
972 [SuppressMessage("Microsoft.Concurrency", "CA8001", Justification = "This does not need to be correct when racing with other threads")]
973 protected unsafe void WriteEvent(int eventId, string arg1, int arg2, int arg3)
975 if (m_eventSourceEnabled)
977 if (arg1 == null) arg1 = "";
978 fixed (char* string1Bytes = arg1)
980 EventSource.EventData* descrs = stackalloc EventSource.EventData[3];
981 descrs[0].DataPointer = (IntPtr)string1Bytes;
982 descrs[0].Size = ((arg1.Length + 1) * 2);
983 descrs[1].DataPointer = (IntPtr)(&arg2);
985 descrs[2].DataPointer = (IntPtr)(&arg3);
987 WriteEventCore(eventId, 3, descrs);
992 // optimized for common signatures (string and longs)
993 [SecuritySafeCritical]
994 [SuppressMessage("Microsoft.Concurrency", "CA8001", Justification = "This does not need to be correct when racing with other threads")]
995 protected unsafe void WriteEvent(int eventId, string arg1, long arg2)
997 if (m_eventSourceEnabled)
999 if (arg1 == null) arg1 = "";
1000 fixed (char* string1Bytes = arg1)
1002 EventSource.EventData* descrs = stackalloc EventSource.EventData[2];
1003 descrs[0].DataPointer = (IntPtr)string1Bytes;
1004 descrs[0].Size = ((arg1.Length + 1) * 2);
1005 descrs[1].DataPointer = (IntPtr)(&arg2);
1007 WriteEventCore(eventId, 2, descrs);
1012 // optimized for common signatures (long and string)
1013 [SecuritySafeCritical]
1014 [SuppressMessage("Microsoft.Concurrency", "CA8001", Justification = "This does not need to be correct when racing with other threads")]
1015 protected unsafe void WriteEvent(int eventId, long arg1, string arg2)
1017 if (m_eventSourceEnabled)
1019 if (arg2 == null) arg2 = "";
1020 fixed (char* string2Bytes = arg2)
1022 EventSource.EventData* descrs = stackalloc EventSource.EventData[2];
1023 descrs[0].DataPointer = (IntPtr)(&arg1);
1025 descrs[1].DataPointer = (IntPtr)string2Bytes;
1026 descrs[1].Size = ((arg2.Length + 1) * 2);
1027 WriteEventCore(eventId, 2, descrs);
1032 // optimized for common signatures (int and string)
1033 [SecuritySafeCritical]
1034 [SuppressMessage("Microsoft.Concurrency", "CA8001", Justification = "This does not need to be correct when racing with other threads")]
1035 protected unsafe void WriteEvent(int eventId, int arg1, string arg2)
1037 if (m_eventSourceEnabled)
1039 if (arg2 == null) arg2 = "";
1040 fixed (char* string2Bytes = arg2)
1042 EventSource.EventData* descrs = stackalloc EventSource.EventData[2];
1043 descrs[0].DataPointer = (IntPtr)(&arg1);
1045 descrs[1].DataPointer = (IntPtr)string2Bytes;
1046 descrs[1].Size = ((arg2.Length + 1) * 2);
1047 WriteEventCore(eventId, 2, descrs);
1052 [SecuritySafeCritical]
1053 [SuppressMessage("Microsoft.Concurrency", "CA8001", Justification = "This does not need to be correct when racing with other threads")]
1054 protected unsafe void WriteEvent(int eventId, byte[] arg1)
1056 if (m_eventSourceEnabled)
1058 EventSource.EventData* descrs = stackalloc EventSource.EventData[2];
1059 if (arg1 == null || arg1.Length == 0)
1062 descrs[0].DataPointer = (IntPtr)(&blobSize);
1064 descrs[1].DataPointer = (IntPtr)(&blobSize); // valid address instead of empty content
1066 WriteEventCore(eventId, 2, descrs);
1070 int blobSize = arg1.Length;
1071 fixed (byte* blob = &arg1[0])
1073 descrs[0].DataPointer = (IntPtr)(&blobSize);
1075 descrs[1].DataPointer = (IntPtr)blob;
1076 descrs[1].Size = blobSize;
1077 WriteEventCore(eventId, 2, descrs);
1083 [SecuritySafeCritical]
1084 [SuppressMessage("Microsoft.Concurrency", "CA8001", Justification = "This does not need to be correct when racing with other threads")]
1085 protected unsafe void WriteEvent(int eventId, long arg1, byte[] arg2)
1087 if (m_eventSourceEnabled)
1089 EventSource.EventData* descrs = stackalloc EventSource.EventData[3];
1090 descrs[0].DataPointer = (IntPtr)(&arg1);
1092 if (arg2 == null || arg2.Length == 0)
1095 descrs[1].DataPointer = (IntPtr)(&blobSize);
1097 descrs[2].DataPointer = (IntPtr)(&blobSize); // valid address instead of empty contents
1099 WriteEventCore(eventId, 3, descrs);
1103 int blobSize = arg2.Length;
1104 fixed (byte* blob = &arg2[0])
1106 descrs[1].DataPointer = (IntPtr)(&blobSize);
1108 descrs[2].DataPointer = (IntPtr)blob;
1109 descrs[2].Size = blobSize;
1110 WriteEventCore(eventId, 3, descrs);
1116 #pragma warning restore 1591
1119 /// Used to construct the data structure to be passed to the native ETW APIs - EventWrite and EventWriteTransfer.
1121 protected internal struct EventData
1124 /// Address where the one argument lives (if this points to managed memory you must ensure the
1125 /// managed object is pinned.
1127 public IntPtr DataPointer { get { return (IntPtr)m_Ptr; } set { m_Ptr = unchecked((long)value); } }
1129 /// Size of the argument referenced by DataPointer
1131 public int Size { get { return m_Size; } set { m_Size = value; } }
1135 /// Initializes the members of this EventData object to point at a previously-pinned
1136 /// tracelogging-compatible metadata blob.
1138 /// <param name="pointer">Pinned tracelogging-compatible metadata blob.</param>
1139 /// <param name="size">The size of the metadata blob.</param>
1140 /// <param name="reserved">Value for reserved: 2 for per-provider metadata, 1 for per-event metadata</param>
1142 internal unsafe void SetMetadata(byte* pointer, int size, int reserved)
1144 this.m_Ptr = (long)(ulong)(UIntPtr)pointer;
1146 this.m_Reserved = reserved; // Mark this descriptor as containing tracelogging-compatible metadata.
1149 //Important, we pass this structure directly to the Win32 EventWrite API, so this structure must be layed out exactly
1150 // the way EventWrite wants it.
1151 internal long m_Ptr;
1152 internal int m_Size;
1153 #pragma warning disable 0649
1154 internal int m_Reserved; // Used to pad the size to match the Win32 API
1155 #pragma warning restore 0649
1160 /// This routine allows you to create efficient WriteEvent helpers, however the code that you use to
1161 /// do this, while straightforward, is unsafe.
1165 /// protected unsafe void WriteEvent(int eventId, string arg1, long arg2)
1167 /// if (IsEnabled())
1169 /// if (arg2 == null) arg2 = "";
1170 /// fixed (char* string2Bytes = arg2)
1172 /// EventSource.EventData* descrs = stackalloc EventSource.EventData[2];
1173 /// descrs[0].DataPointer = (IntPtr)(&arg1);
1174 /// descrs[0].Size = 8;
1175 /// descrs[1].DataPointer = (IntPtr)string2Bytes;
1176 /// descrs[1].Size = ((arg2.Length + 1) * 2);
1177 /// WriteEventCore(eventId, 2, descrs);
1184 [CLSCompliant(false)]
1185 protected unsafe void WriteEventCore(int eventId, int eventDataCount, EventSource.EventData* data)
1187 WriteEventWithRelatedActivityIdCore(eventId, null, eventDataCount, data);
1191 /// This routine allows you to create efficient WriteEventWithRelatedActivityId helpers, however the code
1192 /// that you use to do this, while straightforward, is unsafe. The only difference from
1193 /// <see cref="WriteEventCore"/> is that you pass the relatedActivityId from caller through to this API
1197 /// protected unsafe void WriteEventWithRelatedActivityId(int eventId, Guid relatedActivityId, string arg1, long arg2)
1199 /// if (IsEnabled())
1201 /// if (arg2 == null) arg2 = "";
1202 /// fixed (char* string2Bytes = arg2)
1204 /// EventSource.EventData* descrs = stackalloc EventSource.EventData[2];
1205 /// descrs[0].DataPointer = (IntPtr)(&arg1);
1206 /// descrs[0].Size = 8;
1207 /// descrs[1].DataPointer = (IntPtr)string2Bytes;
1208 /// descrs[1].Size = ((arg2.Length + 1) * 2);
1209 /// WriteEventWithRelatedActivityIdCore(eventId, relatedActivityId, 2, descrs);
1216 [CLSCompliant(false)]
1217 protected unsafe void WriteEventWithRelatedActivityIdCore(int eventId, Guid* relatedActivityId, int eventDataCount, EventSource.EventData* data)
1219 if (m_eventSourceEnabled)
1223 Contract.Assert(m_eventData != null); // You must have initialized this if you enabled the source.
1224 if (relatedActivityId != null)
1225 ValidateEventOpcodeForTransfer(ref m_eventData[eventId]);
1227 #if FEATURE_MANAGED_ETW
1228 if (m_eventData[eventId].EnabledForETW)
1230 EventOpcode opcode = (EventOpcode)m_eventData[eventId].Descriptor.Opcode;
1231 EventActivityOptions activityOptions = m_eventData[eventId].ActivityOptions;
1232 Guid* pActivityId = null;
1233 Guid activityId = Guid.Empty;
1234 Guid relActivityId = Guid.Empty;
1236 if (opcode != EventOpcode.Info && relatedActivityId == null &&
1237 ((activityOptions & EventActivityOptions.Disable) == 0))
1239 if (opcode == EventOpcode.Start)
1241 m_activityTracker.OnStart(m_name, m_eventData[eventId].Name, m_eventData[eventId].Descriptor.Task, ref activityId, ref relActivityId, m_eventData[eventId].ActivityOptions);
1243 else if (opcode == EventOpcode.Stop)
1245 m_activityTracker.OnStop(m_name, m_eventData[eventId].Name, m_eventData[eventId].Descriptor.Task, ref activityId);
1248 if (activityId != Guid.Empty)
1249 pActivityId = &activityId;
1250 if (relActivityId != Guid.Empty)
1251 relatedActivityId = &relActivityId;
1254 #if FEATURE_ACTIVITYSAMPLING
1255 // this code should be kept in sync with WriteEventVarargs().
1256 SessionMask etwSessions = SessionMask.All;
1257 // only compute etwSessions if there are *any* ETW filters enabled...
1258 if ((ulong)m_curLiveSessions != 0)
1259 etwSessions = GetEtwSessionMask(eventId, relatedActivityId);
1260 // OutputDebugString(string.Format("{0}.WriteEvent(id {1}) -> to sessions {2:x}",
1261 // m_name, m_eventData[eventId].Name, (ulong) etwSessions));
1263 if ((ulong)etwSessions != 0 || m_legacySessions != null && m_legacySessions.Count > 0)
1265 if (!SelfDescribingEvents)
1267 if (etwSessions.IsEqualOrSupersetOf(m_curLiveSessions))
1269 // OutputDebugString(string.Format(" (1) id {0}, kwd {1:x}",
1270 // m_eventData[eventId].Name, m_eventData[eventId].Descriptor.Keywords));
1271 // by default the Descriptor.Keyword will have the perEventSourceSessionId bit
1272 // mask set to 0x0f so, when all ETW sessions want the event we don't need to
1273 // synthesize a new one
1274 if (!m_provider.WriteEvent(ref m_eventData[eventId].Descriptor, pActivityId, relatedActivityId, eventDataCount, (IntPtr)data))
1275 ThrowEventSourceException();
1279 long origKwd = unchecked((long)((ulong)m_eventData[eventId].Descriptor.Keywords & ~(SessionMask.All.ToEventKeywords())));
1280 // OutputDebugString(string.Format(" (2) id {0}, kwd {1:x}",
1281 // m_eventData[eventId].Name, etwSessions.ToEventKeywords() | (ulong) origKwd));
1282 // only some of the ETW sessions will receive this event. Synthesize a new
1283 // Descriptor whose Keywords field will have the appropriate bits set.
1284 // etwSessions might be 0, if there are legacy ETW listeners that want this event
1285 var desc = new EventDescriptor(
1286 m_eventData[eventId].Descriptor.EventId,
1287 m_eventData[eventId].Descriptor.Version,
1288 m_eventData[eventId].Descriptor.Channel,
1289 m_eventData[eventId].Descriptor.Level,
1290 m_eventData[eventId].Descriptor.Opcode,
1291 m_eventData[eventId].Descriptor.Task,
1292 unchecked((long)etwSessions.ToEventKeywords() | origKwd));
1294 if (!m_provider.WriteEvent(ref desc, pActivityId, relatedActivityId, eventDataCount, (IntPtr)data))
1295 ThrowEventSourceException();
1300 TraceLoggingEventTypes tlet = m_eventData[eventId].TraceLoggingEventTypes;
1303 tlet = new TraceLoggingEventTypes(m_eventData[eventId].Name,
1305 m_eventData[eventId].Parameters);
1306 Interlocked.CompareExchange(ref m_eventData[eventId].TraceLoggingEventTypes, tlet, null);
1309 long origKwd = unchecked((long)((ulong)m_eventData[eventId].Descriptor.Keywords & ~(SessionMask.All.ToEventKeywords())));
1310 // TODO: activity ID support
1311 EventSourceOptions opt = new EventSourceOptions
1313 Keywords = (EventKeywords)unchecked((long)etwSessions.ToEventKeywords() | origKwd),
1314 Level = (EventLevel)m_eventData[eventId].Descriptor.Level,
1315 Opcode = (EventOpcode)m_eventData[eventId].Descriptor.Opcode
1318 WriteMultiMerge(m_eventData[eventId].Name, ref opt, tlet, pActivityId, relatedActivityId, data);
1322 if (!SelfDescribingEvents)
1324 if (!m_provider.WriteEvent(ref m_eventData[eventId].Descriptor, pActivityId, relatedActivityId, eventDataCount, (IntPtr)data))
1325 ThrowEventSourceException();
1329 TraceLoggingEventTypes tlet = m_eventData[eventId].TraceLoggingEventTypes;
1332 tlet = new TraceLoggingEventTypes(m_eventData[eventId].Name,
1333 m_eventData[eventId].Tags,
1334 m_eventData[eventId].Parameters);
1335 Interlocked.CompareExchange(ref m_eventData[eventId].TraceLoggingEventTypes, tlet, null);
1338 EventSourceOptions opt = new EventSourceOptions
1340 Keywords = (EventKeywords)m_eventData[eventId].Descriptor.Keywords,
1341 Level = (EventLevel)m_eventData[eventId].Descriptor.Level,
1342 Opcode = (EventOpcode)m_eventData[eventId].Descriptor.Opcode
1345 WriteMultiMerge(m_eventData[eventId].Name, ref opt, tlet, pActivityId, relatedActivityId, data);
1347 #endif // FEATURE_ACTIVITYSAMPLING
1349 #endif // FEATURE_MANAGED_ETW
1351 if (m_Dispatchers != null && m_eventData[eventId].EnabledForAnyListener)
1352 WriteToAllListeners(eventId, relatedActivityId, eventDataCount, data);
1354 catch (Exception ex)
1356 if (ex is EventSourceException)
1359 ThrowEventSourceException(ex);
1364 // fallback varags helpers.
1366 /// This is the varargs helper for writing an event. It does create an array and box all the arguments so it is
1367 /// relatively inefficient and should only be used for relatively rare events (e.g. less than 100 / sec). If your
1368 /// rates are faster than that you should use <see cref="WriteEventCore"/> to create fast helpers for your particular
1369 /// method signature. Even if you use this for rare events, this call should be guarded by an <see cref="IsEnabled()"/>
1370 /// check so that the varargs call is not made when the EventSource is not active.
1372 [SecuritySafeCritical]
1373 [SuppressMessage("Microsoft.Concurrency", "CA8001", Justification = "This does not need to be correct when racing with other threads")]
1374 protected unsafe void WriteEvent(int eventId, params object[] args)
1376 WriteEventVarargs(eventId, null, args);
1380 /// This is the varargs helper for writing an event which also specifies a related activity. It is completely analogous
1381 /// to corresponding WriteEvent (they share implementation). It does create an array and box all the arguments so it is
1382 /// relatively inefficient and should only be used for relatively rare events (e.g. less than 100 / sec). If your
1383 /// rates are faster than that you should use <see cref="WriteEventWithRelatedActivityIdCore"/> to create fast helpers for your
1384 /// particular method signature. Even if you use this for rare events, this call should be guarded by an <see cref="IsEnabled()"/>
1385 /// check so that the varargs call is not made when the EventSource is not active.
1387 [SecuritySafeCritical]
1388 protected unsafe void WriteEventWithRelatedActivityId(int eventId, Guid relatedActivityId, params object[] args)
1390 WriteEventVarargs(eventId, &relatedActivityId, args);
1395 #region IDisposable Members
1397 /// Disposes of an EventSource.
1399 public void Dispose()
1402 GC.SuppressFinalize(this);
1405 /// Disposes of an EventSource.
1408 /// Called from Dispose() with disposing=true, and from the finalizer (~EventSource) with disposing=false.
1410 /// 1. We may be called more than once: do nothing after the first call.
1411 /// 2. Avoid throwing exceptions if disposing is false, i.e. if we're being finalized.
1413 /// <param name="disposing">True if called from Dispose(), false if called from the finalizer.</param>
1414 protected virtual void Dispose(bool disposing)
1418 #if FEATURE_MANAGED_ETW
1419 // Send the manifest one more time to ensure circular buffers have a chance to get to this information
1420 // even in scenarios with a high volume of ETW events.
1421 if (m_eventSourceEnabled)
1425 SendManifest(m_rawManifest);
1428 { } // If it fails, simply give up.
1429 m_eventSourceEnabled = false;
1431 if (m_provider != null)
1433 m_provider.Dispose();
1438 m_eventSourceEnabled = false;
1441 /// Finalizer for EventSource
1445 this.Dispose(false);
1450 #if FEATURE_ACTIVITYSAMPLING
1451 internal void WriteStringToListener(EventListener listener, string msg, SessionMask m)
1453 Contract.Assert(listener == null || (uint)m == (uint)SessionMask.FromId(0));
1455 if (m_eventSourceEnabled)
1457 if (listener == null)
1459 WriteEventString(0, unchecked((long)m.ToEventKeywords()), msg);
1463 List<object> arg = new List<object>();
1465 EventWrittenEventArgs eventCallbackArgs = new EventWrittenEventArgs(this);
1466 eventCallbackArgs.EventId = 0;
1467 eventCallbackArgs.Payload = new ReadOnlyCollection<object>(arg);
1468 listener.OnEventWritten(eventCallbackArgs);
1474 private unsafe void WriteEventRaw(
1475 ref EventDescriptor eventDescriptor,
1477 Guid* relatedActivityID,
1481 #if FEATURE_MANAGED_ETW
1482 if (m_provider == null)
1484 ThrowEventSourceException();
1488 if (!m_provider.WriteEventRaw(ref eventDescriptor, activityID, relatedActivityID, dataCount, data))
1489 ThrowEventSourceException();
1491 #endif // FEATURE_MANAGED_ETW
1494 // FrameworkEventSource is on the startup path for the framework, so we have this internal overload that it can use
1495 // to prevent the working set hit from looking at the custom attributes on the type to get the Guid.
1496 internal EventSource(Guid eventSourceGuid, string eventSourceName)
1497 : this(eventSourceGuid, eventSourceName, EventSourceSettings.EtwManifestEventFormat)
1500 // Used by the internal FrameworkEventSource constructor and the TraceLogging-style event source constructor
1501 internal EventSource(Guid eventSourceGuid, string eventSourceName, EventSourceSettings settings, string[] traits = null)
1503 m_config = ValidateSettings(settings);
1504 Initialize(eventSourceGuid, eventSourceName, traits);
1508 /// This method is responsible for the common initialization path from our constructors. It must
1509 /// not leak any exceptions (otherwise, since most EventSource classes define a static member,
1510 /// "Log", such an exception would become a cached exception for the initialization of the static
1511 /// member, and any future access to the "Log" would throw the cached exception).
1513 [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1720:IdentifiersShouldNotContainTypeNames", MessageId = "guid")]
1514 [SecuritySafeCritical]
1515 private unsafe void Initialize(Guid eventSourceGuid, string eventSourceName, string[] traits)
1520 if (m_traits != null && m_traits.Length % 2 != 0)
1521 throw new ArgumentException(Environment.GetResourceString("TraitEven"), "traits");
1523 if (eventSourceGuid == Guid.Empty)
1524 throw new ArgumentException(Environment.GetResourceString("EventSource_NeedGuid"));
1526 if (eventSourceName == null)
1527 throw new ArgumentException(Environment.GetResourceString("EventSource_NeedName"));
1529 m_name = eventSourceName;
1530 m_guid = eventSourceGuid;
1531 #if FEATURE_ACTIVITYSAMPLING
1532 m_curLiveSessions = new SessionMask(0);
1533 m_etwSessionIdMap = new EtwSession[SessionMask.MAX];
1534 #endif // FEATURE_ACTIVITYSAMPLING
1536 //Enable Implicit Activity tracker
1537 m_activityTracker = ActivityTracker.Instance;
1539 #if FEATURE_MANAGED_ETW
1540 // Create and register our provider traits. We do this early because it is needed to log errors
1541 // In the self-describing event case.
1542 this.InitializeProviderMetadata();
1544 // Register the provider with ETW
1545 var provider = new OverideEventProvider(this);
1546 provider.Register(eventSourceGuid);
1548 // Add the eventSource to the global (weak) list.
1549 // This also sets m_id, which is the index in the list.
1550 EventListener.AddEventSource(this);
1552 #if FEATURE_MANAGED_ETW
1553 // OK if we get this far without an exception, then we can at least write out error messages.
1554 // Set m_provider, which allows this.
1555 m_provider = provider;
1557 #if !ES_BUILD_STANDALONE
1558 // API available on OS >= Win 8 and patched Win 7.
1559 // Disable only for FrameworkEventSource to avoid recursion inside exception handling.
1560 var osVer = Environment.OSVersion.Version.Major * 10 + Environment.OSVersion.Version.Minor;
1561 if (this.Name != "System.Diagnostics.Eventing.FrameworkEventSource" || osVer >= 62)
1564 int setInformationResult;
1565 fixed (void* providerMetadata = this.providerMetadata)
1567 setInformationResult = m_provider.SetInformation(
1568 UnsafeNativeMethods.ManifestEtw.EVENT_INFO_CLASS.SetTraits,
1570 this.providerMetadata.Length);
1573 #endif // FEATURE_MANAGED_ETW
1575 Contract.Assert(!m_eventSourceEnabled); // We can't be enabled until we are completely initted.
1576 // We are logically completely initialized at this point.
1577 m_completelyInited = true;
1581 if (m_constructionException == null)
1582 m_constructionException = e;
1583 ReportOutOfBandMessage("ERROR: Exception during construction of EventSource " + Name + ": " + e.Message, true);
1586 // Once m_completelyInited is set, you can have concurrency, so all work is under the lock.
1587 lock (EventListener.EventListenersLock)
1589 // If there are any deferred commands, we can do them now.
1590 // This is the most likely place for exceptions to happen.
1591 // Note that we are NOT resetting m_deferredCommands to NULL here,
1592 // We are giving for EventHandler<EventCommandEventArgs> that will be attached later
1593 EventCommandEventArgs deferredCommands = m_deferredCommands;
1594 while (deferredCommands != null)
1596 DoCommand(deferredCommands); // This can never throw, it catches them and reports the errors.
1597 deferredCommands = deferredCommands.nextCommand;
1602 private static string GetName(Type eventSourceType, EventManifestOptions flags)
1604 if (eventSourceType == null)
1605 throw new ArgumentNullException("eventSourceType");
1606 Contract.EndContractBlock();
1608 EventSourceAttribute attrib = (EventSourceAttribute)GetCustomAttributeHelper(eventSourceType, typeof(EventSourceAttribute), flags);
1609 if (attrib != null && attrib.Name != null)
1612 return eventSourceType.Name;
1616 /// Implements the SHA1 hashing algorithm. Note that this
1617 /// implementation is for hashing public information. Do not
1618 /// use this code to hash private data, as this implementation does
1619 /// not take any steps to avoid information disclosure.
1621 private struct Sha1ForNonSecretPurposes
1623 private long length; // Total message length in bits
1624 private uint[] w; // Workspace
1625 private int pos; // Length of current chunk in bytes
1628 /// Call Start() to initialize the hash object.
1634 this.w = new uint[85];
1639 this.w[80] = 0x67452301;
1640 this.w[81] = 0xEFCDAB89;
1641 this.w[82] = 0x98BADCFE;
1642 this.w[83] = 0x10325476;
1643 this.w[84] = 0xC3D2E1F0;
1647 /// Adds an input byte to the hash.
1649 /// <param name="input">Data to include in the hash.</param>
1650 public void Append(byte input)
1652 this.w[this.pos / 4] = (this.w[this.pos / 4] << 8) | input;
1653 if (64 == ++this.pos)
1660 /// Adds input bytes to the hash.
1662 /// <param name="input">
1663 /// Data to include in the hash. Must not be null.
1665 public void Append(byte[] input)
1667 foreach (var b in input)
1674 /// Retrieves the hash value.
1675 /// Note that after calling this function, the hash object should
1676 /// be considered uninitialized. Subsequent calls to Append or
1677 /// Finish will produce useless results. Call Start() to
1680 /// <param name="output">
1681 /// Buffer to receive the hash value. Must not be null.
1682 /// Up to 20 bytes of hash will be written to the output buffer.
1683 /// If the buffer is smaller than 20 bytes, the remaining hash
1684 /// bytes will be lost. If the buffer is larger than 20 bytes, the
1685 /// rest of the buffer is left unmodified.
1687 public void Finish(byte[] output)
1689 long l = this.length + 8 * this.pos;
1691 while (this.pos != 56)
1698 this.Append((byte)(l >> 56));
1699 this.Append((byte)(l >> 48));
1700 this.Append((byte)(l >> 40));
1701 this.Append((byte)(l >> 32));
1702 this.Append((byte)(l >> 24));
1703 this.Append((byte)(l >> 16));
1704 this.Append((byte)(l >> 8));
1705 this.Append((byte)l);
1707 int end = output.Length < 20 ? output.Length : 20;
1708 for (int i = 0; i != end; i++)
1710 uint temp = this.w[80 + i / 4];
1711 output[i] = (byte)(temp >> 24);
1712 this.w[80 + i / 4] = temp << 8;
1718 /// Called when this.pos reaches 64.
1720 private void Drain()
1722 for (int i = 16; i != 80; i++)
1724 this.w[i] = Rol1((this.w[i - 3] ^ this.w[i - 8] ^ this.w[i - 14] ^ this.w[i - 16]));
1729 uint a = this.w[80];
1730 uint b = this.w[81];
1731 uint c = this.w[82];
1732 uint d = this.w[83];
1733 uint e = this.w[84];
1735 for (int i = 0; i != 20; i++)
1737 const uint k = 0x5A827999;
1738 uint f = (b & c) | ((~b) & d);
1739 uint temp = Rol5(a) + f + e + k + this.w[i]; e = d; d = c; c = Rol30(b); b = a; a = temp;
1742 for (int i = 20; i != 40; i++)
1745 const uint k = 0x6ED9EBA1;
1746 uint temp = Rol5(a) + f + e + k + this.w[i]; e = d; d = c; c = Rol30(b); b = a; a = temp;
1749 for (int i = 40; i != 60; i++)
1751 uint f = (b & c) | (b & d) | (c & d);
1752 const uint k = 0x8F1BBCDC;
1753 uint temp = Rol5(a) + f + e + k + this.w[i]; e = d; d = c; c = Rol30(b); b = a; a = temp;
1756 for (int i = 60; i != 80; i++)
1759 const uint k = 0xCA62C1D6;
1760 uint temp = Rol5(a) + f + e + k + this.w[i]; e = d; d = c; c = Rol30(b); b = a; a = temp;
1770 this.length += 512; // 64 bytes == 512 bits
1774 private static uint Rol1(uint input)
1776 return (input << 1) | (input >> 31);
1779 private static uint Rol5(uint input)
1781 return (input << 5) | (input >> 27);
1784 private static uint Rol30(uint input)
1786 return (input << 30) | (input >> 2);
1790 private static Guid GenerateGuidFromName(string name)
1792 byte[] bytes = Encoding.BigEndianUnicode.GetBytes(name);
1793 var hash = new Sha1ForNonSecretPurposes();
1795 hash.Append(namespaceBytes);
1797 Array.Resize(ref bytes, 16);
1800 bytes[7] = unchecked((byte)((bytes[7] & 0x0F) | 0x50)); // Set high 4 bits of octet 7 to 5, as per RFC 4122
1801 return new Guid(bytes);
1805 private unsafe object DecodeObject(int eventId, int parameterId, ref EventSource.EventData* data)
1807 // TODO FIX : We use reflection which in turn uses EventSource, right now we carefully avoid
1808 // the recursion, but can we do this in a robust way?
1810 IntPtr dataPointer = data->DataPointer;
1811 // advance to next EventData in array
1814 Type dataType = m_eventData[eventId].Parameters[parameterId].ParameterType;
1817 if (dataType == typeof(IntPtr))
1819 return *((IntPtr*)dataPointer);
1821 else if (dataType == typeof(int))
1823 return *((int*)dataPointer);
1825 else if (dataType == typeof(uint))
1827 return *((uint*)dataPointer);
1829 else if (dataType == typeof(long))
1831 return *((long*)dataPointer);
1833 else if (dataType == typeof(ulong))
1835 return *((ulong*)dataPointer);
1837 else if (dataType == typeof(byte))
1839 return *((byte*)dataPointer);
1841 else if (dataType == typeof(sbyte))
1843 return *((sbyte*)dataPointer);
1845 else if (dataType == typeof(short))
1847 return *((short*)dataPointer);
1849 else if (dataType == typeof(ushort))
1851 return *((ushort*)dataPointer);
1853 else if (dataType == typeof(float))
1855 return *((float*)dataPointer);
1857 else if (dataType == typeof(double))
1859 return *((double*)dataPointer);
1861 else if (dataType == typeof(decimal))
1863 return *((decimal*)dataPointer);
1865 else if (dataType == typeof(bool))
1867 // The manifest defines a bool as a 32bit type (WIN32 BOOL), not 1 bit as CLR Does.
1868 if (*((int*)dataPointer) == 1)
1877 else if (dataType == typeof(Guid))
1879 return *((Guid*)dataPointer);
1881 else if (dataType == typeof(char))
1883 return *((char*)dataPointer);
1885 else if (dataType == typeof(DateTime))
1887 long dateTimeTicks = *((long*)dataPointer);
1888 return DateTime.FromFileTimeUtc(dateTimeTicks);
1890 else if (dataType == typeof(byte[]))
1892 // byte[] are written to EventData* as an int followed by a blob
1893 int cbSize = *((int*)dataPointer);
1894 byte[] blob = new byte[cbSize];
1895 dataPointer = data->DataPointer;
1897 for (int i = 0; i < cbSize; ++i)
1898 blob[i] = *((byte*)dataPointer);
1901 else if (dataType == typeof(byte*))
1903 // TODO: how do we want to handle this? For now we ignore it...
1908 if (dataType.IsEnum())
1910 dataType = Enum.GetUnderlyingType(dataType);
1914 // TODO FIX NOW Assuming that it is a string at this point is really likely to be fragile
1915 // We should do something better.
1917 // Everything else is marshaled as a string.
1918 // ETW strings are NULL-terminated, so marshal everything up to the first
1919 // null in the string.
1920 return System.Runtime.InteropServices.Marshal.PtrToStringUni(dataPointer);
1924 // Finds the Dispatcher (which holds the filtering state), for a given dispatcher for the current
1926 private EventDispatcher GetDispatcher(EventListener listener)
1928 EventDispatcher dispatcher = m_Dispatchers;
1929 while (dispatcher != null)
1931 if (dispatcher.m_Listener == listener)
1933 dispatcher = dispatcher.m_Next;
1939 private unsafe void WriteEventVarargs(int eventId, Guid* childActivityID, object[] args)
1941 if (m_eventSourceEnabled)
1945 Contract.Assert(m_eventData != null); // You must have initialized this if you enabled the source.
1946 if (childActivityID != null)
1948 ValidateEventOpcodeForTransfer(ref m_eventData[eventId]);
1950 // If you use WriteEventWithRelatedActivityID you MUST declare the first argument to be a GUID
1951 // with the name 'relatedActivityID, and NOT pass this argument to the WriteEvent method.
1952 // During manifest creation we modify the ParameterInfo[] that we store to strip out any
1953 // first parameter that is of type Guid and named "relatedActivityId." Thus, if you call
1954 // WriteEventWithRelatedActivityID from a method that doesn't name its first parameter correctly
1955 // we can end up in a state where the ParameterInfo[] doesn't have its first parameter stripped,
1956 // and this leads to a mismatch between the number of arguments and the number of ParameterInfos,
1957 // which would cause a cryptic IndexOutOfRangeException later if we don't catch it here.
1958 if (!m_eventData[eventId].HasRelatedActivityID)
1960 throw new ArgumentException(Environment.GetResourceString("EventSource_NoRelatedActivityId"));
1964 LogEventArgsMismatches(m_eventData[eventId].Parameters, args);
1965 #if FEATURE_MANAGED_ETW
1966 if (m_eventData[eventId].EnabledForETW)
1968 Guid* pActivityId = null;
1969 Guid activityId = Guid.Empty;
1970 Guid relatedActivityId = Guid.Empty;
1971 EventOpcode opcode = (EventOpcode)m_eventData[eventId].Descriptor.Opcode;
1972 EventActivityOptions activityOptions = m_eventData[eventId].ActivityOptions;
1974 if (childActivityID == null &&
1975 ((activityOptions & EventActivityOptions.Disable) == 0))
1977 if (opcode == EventOpcode.Start)
1979 m_activityTracker.OnStart(m_name, m_eventData[eventId].Name, m_eventData[eventId].Descriptor.Task, ref activityId, ref relatedActivityId, m_eventData[eventId].ActivityOptions);
1981 else if (opcode == EventOpcode.Stop)
1983 m_activityTracker.OnStop(m_name, m_eventData[eventId].Name, m_eventData[eventId].Descriptor.Task, ref activityId);
1986 if (activityId != Guid.Empty)
1987 pActivityId = &activityId;
1988 if (relatedActivityId != Guid.Empty)
1989 childActivityID = &relatedActivityId;
1992 #if FEATURE_ACTIVITYSAMPLING
1993 // this code should be kept in sync with WriteEventWithRelatedActivityIdCore().
1994 SessionMask etwSessions = SessionMask.All;
1995 // only compute etwSessions if there are *any* ETW filters enabled...
1996 if ((ulong)m_curLiveSessions != 0)
1997 etwSessions = GetEtwSessionMask(eventId, childActivityID);
1999 if ((ulong)etwSessions != 0 || m_legacySessions != null && m_legacySessions.Count > 0)
2001 if (!SelfDescribingEvents)
2003 if (etwSessions.IsEqualOrSupersetOf(m_curLiveSessions))
2005 // by default the Descriptor.Keyword will have the perEventSourceSessionId bit
2006 // mask set to 0x0f so, when all ETW sessions want the event we don't need to
2007 // synthesize a new one
2008 if (!m_provider.WriteEvent(ref m_eventData[eventId].Descriptor, pActivityId, childActivityID, args))
2009 ThrowEventSourceException();
2013 long origKwd = unchecked((long)((ulong)m_eventData[eventId].Descriptor.Keywords & ~(SessionMask.All.ToEventKeywords())));
2014 // only some of the ETW sessions will receive this event. Synthesize a new
2015 // Descriptor whose Keywords field will have the appropriate bits set.
2016 var desc = new EventDescriptor(
2017 m_eventData[eventId].Descriptor.EventId,
2018 m_eventData[eventId].Descriptor.Version,
2019 m_eventData[eventId].Descriptor.Channel,
2020 m_eventData[eventId].Descriptor.Level,
2021 m_eventData[eventId].Descriptor.Opcode,
2022 m_eventData[eventId].Descriptor.Task,
2023 unchecked((long)(ulong)etwSessions | origKwd));
2025 if (!m_provider.WriteEvent(ref desc, pActivityId, childActivityID, args))
2026 ThrowEventSourceException();
2031 TraceLoggingEventTypes tlet = m_eventData[eventId].TraceLoggingEventTypes;
2034 tlet = new TraceLoggingEventTypes(m_eventData[eventId].Name,
2036 m_eventData[eventId].Parameters);
2037 Interlocked.CompareExchange(ref m_eventData[eventId].TraceLoggingEventTypes, tlet, null);
2040 long origKwd = unchecked((long)((ulong)m_eventData[eventId].Descriptor.Keywords & ~(SessionMask.All.ToEventKeywords())));
2041 // TODO: activity ID support
2042 EventSourceOptions opt = new EventSourceOptions
2044 Keywords = (EventKeywords)unchecked((long)(ulong)etwSessions | origKwd),
2045 Level = (EventLevel)m_eventData[eventId].Descriptor.Level,
2046 Opcode = (EventOpcode)m_eventData[eventId].Descriptor.Opcode
2049 WriteMultiMerge(m_eventData[eventId].Name, ref opt, tlet, pActivityId, childActivityID, args);
2053 if (!SelfDescribingEvents)
2055 if (!m_provider.WriteEvent(ref m_eventData[eventId].Descriptor, pActivityId, childActivityID, args))
2056 ThrowEventSourceException();
2060 TraceLoggingEventTypes tlet = m_eventData[eventId].TraceLoggingEventTypes;
2063 tlet = new TraceLoggingEventTypes(m_eventData[eventId].Name,
2065 m_eventData[eventId].Parameters);
2066 Interlocked.CompareExchange(ref m_eventData[eventId].TraceLoggingEventTypes, tlet, null);
2069 // TODO: activity ID support
2070 EventSourceOptions opt = new EventSourceOptions
2072 Keywords = (EventKeywords)m_eventData[eventId].Descriptor.Keywords,
2073 Level = (EventLevel)m_eventData[eventId].Descriptor.Level,
2074 Opcode = (EventOpcode)m_eventData[eventId].Descriptor.Opcode
2077 WriteMultiMerge(m_eventData[eventId].Name, ref opt, tlet, pActivityId, childActivityID, args);
2079 #endif // FEATURE_ACTIVITYSAMPLING
2081 #endif // FEATURE_MANAGED_ETW
2082 if (m_Dispatchers != null && m_eventData[eventId].EnabledForAnyListener)
2084 #if !ES_BUILD_STANDALONE
2085 // Maintain old behavior - object identity is preserved
2086 if (AppContextSwitches.PreserveEventListnerObjectIdentity)
2088 WriteToAllListeners(eventId, childActivityID, args);
2091 #endif // !ES_BUILD_STANDALONE
2093 object[] serializedArgs = SerializeEventArgs(eventId, args);
2094 WriteToAllListeners(eventId, childActivityID, serializedArgs);
2098 catch (Exception ex)
2100 if (ex is EventSourceException)
2103 ThrowEventSourceException(ex);
2109 unsafe private object[] SerializeEventArgs(int eventId, object[] args)
2111 TraceLoggingEventTypes eventTypes = m_eventData[eventId].TraceLoggingEventTypes;
2112 if (eventTypes == null)
2114 eventTypes = new TraceLoggingEventTypes(m_eventData[eventId].Name,
2116 m_eventData[eventId].Parameters);
2117 Interlocked.CompareExchange(ref m_eventData[eventId].TraceLoggingEventTypes, eventTypes, null);
2119 var eventData = new object[eventTypes.typeInfos.Length];
2120 for (int i = 0; i < eventTypes.typeInfos.Length; i++)
2122 eventData[i] = eventTypes.typeInfos[i].GetData(args[i]);
2128 /// We expect that the arguments to the Event method and the arguments to WriteEvent match. This function
2129 /// checks that they in fact match and logs a warning to the debugger if they don't.
2131 /// <param name="infos"></param>
2132 /// <param name="args"></param>
2133 private void LogEventArgsMismatches(ParameterInfo[] infos, object[] args)
2136 // It would be nice to have this on PCL builds, but it would be pointless since there isn't support for
2137 // writing to the debugger log on PCL.
2138 bool typesMatch = args.Length == infos.Length;
2141 while (typesMatch && i < args.Length)
2143 Type pType = infos[i].ParameterType;
2145 // Checking to see if the Parameter types (from the Event method) match the supplied argument types.
2146 // Fail if one of two things hold : either the argument type is not equal to the parameter type, or the
2147 // argument is null and the parameter type is non-nullable.
2148 if ((args[i] != null && (args[i].GetType() != pType))
2149 || (args[i] == null && (!(pType.IsGenericType && pType.GetGenericTypeDefinition() == typeof(Nullable<>))))
2161 System.Diagnostics.Debugger.Log(0, null, Environment.GetResourceString("EventSource_VarArgsParameterMismatch") + "\r\n");
2163 #endif //!ES_BUILD_PCL
2167 unsafe private void WriteToAllListeners(int eventId, Guid* childActivityID, int eventDataCount, EventSource.EventData* data)
2169 int paramCount = m_eventData[eventId].Parameters.Length;
2170 if (eventDataCount != paramCount)
2172 ReportOutOfBandMessage(Environment.GetResourceString("EventSource_EventParametersMismatch", eventId, eventDataCount, paramCount), true);
2173 paramCount = Math.Min(paramCount, eventDataCount);
2176 object[] args = new object[paramCount];
2178 EventSource.EventData* dataPtr = data;
2179 for (int i = 0; i < paramCount; i++)
2180 args[i] = DecodeObject(eventId, i, ref dataPtr);
2181 WriteToAllListeners(eventId, childActivityID, args);
2184 // helper for writing to all EventListeners attached the current eventSource.
2186 unsafe private void WriteToAllListeners(int eventId, Guid* childActivityID, params object[] args)
2188 EventWrittenEventArgs eventCallbackArgs = new EventWrittenEventArgs(this);
2189 eventCallbackArgs.EventId = eventId;
2190 if (childActivityID != null)
2191 eventCallbackArgs.RelatedActivityId = *childActivityID;
2192 eventCallbackArgs.EventName = m_eventData[eventId].Name;
2193 eventCallbackArgs.Message = m_eventData[eventId].Message;
2194 eventCallbackArgs.Payload = new ReadOnlyCollection<object>(args);
2196 DispatchToAllListeners(eventId, childActivityID, eventCallbackArgs);
2200 private unsafe void DispatchToAllListeners(int eventId, Guid* childActivityID, EventWrittenEventArgs eventCallbackArgs)
2202 Exception lastThrownException = null;
2203 for (EventDispatcher dispatcher = m_Dispatchers; dispatcher != null; dispatcher = dispatcher.m_Next)
2205 Contract.Assert(dispatcher.m_EventEnabled != null);
2206 if (eventId == -1 || dispatcher.m_EventEnabled[eventId])
2208 #if FEATURE_ACTIVITYSAMPLING
2209 var activityFilter = dispatcher.m_Listener.m_activityFilter;
2210 // order below is important as PassesActivityFilter will "flow" active activities
2211 // even when the current EventSource doesn't have filtering enabled. This allows
2212 // interesting activities to be updated so that sources that do sample can get
2214 if (activityFilter == null ||
2215 ActivityFilter.PassesActivityFilter(activityFilter, childActivityID,
2216 m_eventData[eventId].TriggersActivityTracking > 0,
2218 !dispatcher.m_activityFilteringEnabled)
2219 #endif // FEATURE_ACTIVITYSAMPLING
2223 dispatcher.m_Listener.OnEventWritten(eventCallbackArgs);
2227 ReportOutOfBandMessage("ERROR: Exception during EventSource.OnEventWritten: "
2228 + e.Message, false);
2229 lastThrownException = e;
2235 if (lastThrownException != null)
2237 throw new EventSourceException(lastThrownException);
2241 [SecuritySafeCritical]
2242 [SuppressMessage("Microsoft.Concurrency", "CA8001", Justification = "This does not need to be correct when racing with other threads")]
2243 private unsafe void WriteEventString(EventLevel level, long keywords, string msgString)
2245 #if FEATURE_MANAGED_ETW
2246 if (m_provider != null)
2248 string eventName = "EventSourceMessage";
2249 if (SelfDescribingEvents)
2251 EventSourceOptions opt = new EventSourceOptions
2253 Keywords = (EventKeywords)unchecked(keywords),
2256 var msg = new { message = msgString };
2257 var tlet = new TraceLoggingEventTypes(eventName, EventTags.None, new Type[] { msg.GetType() });
2258 WriteMultiMergeInner(eventName, ref opt, tlet, null, null, msg);
2262 // We want the name of the provider to show up so if we don't have a manifest we create
2263 // on that at least has the provider name (I don't define any events).
2264 if (m_rawManifest == null && m_outOfBandMessageCount == 1)
2266 ManifestBuilder manifestBuilder = new ManifestBuilder(Name, Guid, Name, null, EventManifestOptions.None);
2267 manifestBuilder.StartEvent(eventName, new EventAttribute(0) { Level = EventLevel.LogAlways, Task = (EventTask)0xFFFE });
2268 manifestBuilder.AddEventParameter(typeof(string), "message");
2269 manifestBuilder.EndEvent();
2270 SendManifest(manifestBuilder.CreateManifest());
2273 // We use this low level routine to to bypass the enabled checking, since the eventSource itself is only partially inited.
2274 fixed (char* msgStringPtr = msgString)
2276 EventDescriptor descr = new EventDescriptor(0, 0, 0, (byte)level, 0, 0, keywords);
2277 EventProvider.EventData data = new EventProvider.EventData();
2278 data.Ptr = (ulong)msgStringPtr;
2279 data.Size = (uint)(2 * (msgString.Length + 1));
2281 m_provider.WriteEvent(ref descr, null, null, 1, (IntPtr)((void*)&data));
2285 #endif // FEATURE_MANAGED_ETW
2289 /// Since this is a means of reporting errors (see ReportoutOfBandMessage) any failure encountered
2290 /// while writing the message to any one of the listeners will be silently ignored.
2292 private void WriteStringToAllListeners(string eventName, string msg)
2294 EventWrittenEventArgs eventCallbackArgs = new EventWrittenEventArgs(this);
2295 eventCallbackArgs.EventId = 0;
2296 eventCallbackArgs.Message = msg;
2297 eventCallbackArgs.Payload = new ReadOnlyCollection<object>(new List<object>() { msg });
2298 eventCallbackArgs.PayloadNames = new ReadOnlyCollection<string>(new List<string> { "message" });
2299 eventCallbackArgs.EventName = eventName;
2301 for (EventDispatcher dispatcher = m_Dispatchers; dispatcher != null; dispatcher = dispatcher.m_Next)
2303 bool dispatcherEnabled = false;
2304 if (dispatcher.m_EventEnabled == null)
2306 // if the listeners that weren't correctly initialized, we will send to it
2307 // since this is an error message and we want to see it go out.
2308 dispatcherEnabled = true;
2312 // if there's *any* enabled event on the dispatcher we'll write out the string
2313 // otherwise we'll treat the listener as disabled and skip it
2314 for (int evtId = 0; evtId < dispatcher.m_EventEnabled.Length; ++evtId)
2316 if (dispatcher.m_EventEnabled[evtId])
2318 dispatcherEnabled = true;
2325 if (dispatcherEnabled)
2326 dispatcher.m_Listener.OnEventWritten(eventCallbackArgs);
2330 // ignore any exceptions thrown by listeners' OnEventWritten
2335 #if FEATURE_ACTIVITYSAMPLING
2337 unsafe private SessionMask GetEtwSessionMask(int eventId, Guid* childActivityID)
2339 SessionMask etwSessions = new SessionMask();
2341 for (int i = 0; i < SessionMask.MAX; ++i)
2343 EtwSession etwSession = m_etwSessionIdMap[i];
2344 if (etwSession != null)
2346 ActivityFilter activityFilter = etwSession.m_activityFilter;
2347 // PassesActivityFilter() will flow "interesting" activities, so make sure
2348 // to perform this test first, before ORing with ~m_activityFilteringForETWEnabled
2349 // (note: the first test for !m_activityFilteringForETWEnabled[i] ensures we
2350 // do not fire events indiscriminately, when no filters are specified, but only
2351 // if, in addition, the session did not also enable ActivitySampling)
2352 if (activityFilter == null && !m_activityFilteringForETWEnabled[i] ||
2353 activityFilter != null &&
2354 ActivityFilter.PassesActivityFilter(activityFilter, childActivityID,
2355 m_eventData[eventId].TriggersActivityTracking > 0, this, eventId) ||
2356 !m_activityFilteringForETWEnabled[i])
2358 etwSessions[i] = true;
2362 // flow "interesting" activities for all legacy sessions in which there's some
2363 // level of activity tracing enabled (even other EventSources)
2364 if (m_legacySessions != null && m_legacySessions.Count > 0 &&
2365 (EventOpcode)m_eventData[eventId].Descriptor.Opcode == EventOpcode.Send)
2367 // only calculate InternalCurrentThreadActivityId once
2368 Guid* pCurrentActivityId = null;
2369 Guid currentActivityId;
2370 foreach (var legacyEtwSession in m_legacySessions)
2372 if (legacyEtwSession == null)
2375 ActivityFilter activityFilter = legacyEtwSession.m_activityFilter;
2376 if (activityFilter != null)
2378 if (pCurrentActivityId == null)
2380 currentActivityId = InternalCurrentThreadActivityId;
2381 pCurrentActivityId = ¤tActivityId;
2383 ActivityFilter.FlowActivityIfNeeded(activityFilter, pCurrentActivityId, childActivityID);
2390 #endif // FEATURE_ACTIVITYSAMPLING
2393 /// Returns true if 'eventNum' is enabled if you only consider the level and matchAnyKeyword filters.
2394 /// It is possible that eventSources turn off the event based on additional filtering criteria.
2396 private bool IsEnabledByDefault(int eventNum, bool enable, EventLevel currentLevel, EventKeywords currentMatchAnyKeyword)
2401 EventLevel eventLevel = (EventLevel)m_eventData[eventNum].Descriptor.Level;
2402 EventKeywords eventKeywords = unchecked((EventKeywords)((ulong)m_eventData[eventNum].Descriptor.Keywords & (~(SessionMask.All.ToEventKeywords()))));
2404 #if FEATURE_MANAGED_ETW_CHANNELS
2405 EventChannel channel = unchecked((EventChannel)m_eventData[eventNum].Descriptor.Channel);
2407 EventChannel channel = EventChannel.None;
2410 return IsEnabledCommon(enable, currentLevel, currentMatchAnyKeyword, eventLevel, eventKeywords, channel);
2413 private bool IsEnabledCommon(bool enabled, EventLevel currentLevel, EventKeywords currentMatchAnyKeyword,
2414 EventLevel eventLevel, EventKeywords eventKeywords, EventChannel eventChannel)
2419 // does is pass the level test?
2420 if ((currentLevel != 0) && (currentLevel < eventLevel))
2423 // if yes, does it pass the keywords test?
2424 if (currentMatchAnyKeyword != 0 && eventKeywords != 0)
2426 #if FEATURE_MANAGED_ETW_CHANNELS
2427 // is there a channel with keywords that match currentMatchAnyKeyword?
2428 if (eventChannel != EventChannel.None && this.m_channelData != null && this.m_channelData.Length > (int)eventChannel)
2430 EventKeywords channel_keywords = unchecked((EventKeywords)(m_channelData[(int)eventChannel] | (ulong)eventKeywords));
2431 if (channel_keywords != 0 && (channel_keywords & currentMatchAnyKeyword) == 0)
2437 if ((unchecked((ulong)eventKeywords & (ulong)currentMatchAnyKeyword)) == 0)
2444 [System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.NoInlining)]
2445 private void ThrowEventSourceException(Exception innerEx = null)
2447 // If we fail during out of band logging we may end up trying
2448 // to throw another EventSourceException, thus hitting a StackOverflowException.
2449 // Avoid StackOverflow by making sure we do not recursively call this method.
2450 if (m_EventSourceExceptionRecurenceCount > 0)
2454 m_EventSourceExceptionRecurenceCount++;
2456 // TODO Create variations of EventSourceException that indicate more information using the error code.
2457 switch (EventProvider.GetLastWriteEventError())
2459 case EventProvider.WriteEventErrorCode.EventTooBig:
2460 ReportOutOfBandMessage("EventSourceException: " + Environment.GetResourceString("EventSource_EventTooBig"), true);
2461 if (ThrowOnEventWriteErrors) throw new EventSourceException(Environment.GetResourceString("EventSource_EventTooBig"), innerEx);
2463 case EventProvider.WriteEventErrorCode.NoFreeBuffers:
2464 ReportOutOfBandMessage("EventSourceException: " + Environment.GetResourceString("EventSource_NoFreeBuffers"), true);
2465 if (ThrowOnEventWriteErrors) throw new EventSourceException(Environment.GetResourceString("EventSource_NoFreeBuffers"), innerEx);
2467 case EventProvider.WriteEventErrorCode.NullInput:
2468 ReportOutOfBandMessage("EventSourceException: " + Environment.GetResourceString("EventSource_NullInput"), true);
2469 if (ThrowOnEventWriteErrors) throw new EventSourceException(Environment.GetResourceString("EventSource_NullInput"), innerEx);
2471 case EventProvider.WriteEventErrorCode.TooManyArgs:
2472 ReportOutOfBandMessage("EventSourceException: " + Environment.GetResourceString("EventSource_TooManyArgs"), true);
2473 if (ThrowOnEventWriteErrors) throw new EventSourceException(Environment.GetResourceString("EventSource_TooManyArgs"), innerEx);
2476 if (innerEx != null)
2477 ReportOutOfBandMessage("EventSourceException: " + innerEx.GetType() + ":" + innerEx.Message, true);
2479 ReportOutOfBandMessage("EventSourceException", true);
2480 if (ThrowOnEventWriteErrors) throw new EventSourceException(innerEx);
2486 m_EventSourceExceptionRecurenceCount--;
2490 private void ValidateEventOpcodeForTransfer(ref EventMetadata eventData)
2492 if ((EventOpcode)eventData.Descriptor.Opcode != EventOpcode.Send &&
2493 (EventOpcode)eventData.Descriptor.Opcode != EventOpcode.Receive &&
2494 (EventOpcode)eventData.Descriptor.Opcode != EventOpcode.Start)
2496 ThrowEventSourceException();
2500 internal static EventOpcode GetOpcodeWithDefault(EventOpcode opcode, string eventName)
2502 if (opcode == EventOpcode.Info && eventName != null)
2504 if (eventName.EndsWith(s_ActivityStartSuffix))
2506 return EventOpcode.Start;
2508 else if (eventName.EndsWith(s_ActivityStopSuffix))
2510 return EventOpcode.Stop;
2517 #if FEATURE_MANAGED_ETW
2519 /// This class lets us hook the 'OnEventCommand' from the eventSource.
2521 private class OverideEventProvider : EventProvider
2523 public OverideEventProvider(EventSource eventSource)
2525 this.m_eventSource = eventSource;
2527 protected override void OnControllerCommand(ControllerCommand command, IDictionary<string, string> arguments,
2528 int perEventSourceSessionId, int etwSessionId)
2530 // We use null to represent the ETW EventListener.
2531 EventListener listener = null;
2532 m_eventSource.SendCommand(listener, perEventSourceSessionId, etwSessionId,
2533 (EventCommand)command, IsEnabled(), Level, MatchAnyKeyword, arguments);
2535 private EventSource m_eventSource;
2540 /// Used to hold all the static information about an event. This includes everything in the event
2541 /// descriptor as well as some stuff we added specifically for EventSource. see the
2542 /// code:m_eventData for where we use this.
2544 internal struct EventMetadata
2546 public EventDescriptor Descriptor;
2547 public EventTags Tags;
2548 public bool EnabledForAnyListener; // true if any dispatcher has this event turned on
2549 public bool EnabledForETW; // is this event on for the OS ETW data dispatcher?
2551 public bool HasRelatedActivityID; // Set if the event method's first parameter is a Guid named 'relatedActivityId'
2552 #if !FEATURE_ACTIVITYSAMPLING
2553 #pragma warning disable 0649
2555 public byte TriggersActivityTracking; // count of listeners that marked this event as trigger for start of activity logging.
2556 #if !FEATURE_ACTIVITYSAMPLING
2557 #pragma warning restore 0649
2559 public string Name; // the name of the event
2560 public string Message; // If the event has a message associated with it, this is it.
2561 public ParameterInfo[] Parameters; // TODO can we remove?
2563 public TraceLoggingEventTypes TraceLoggingEventTypes;
2564 public EventActivityOptions ActivityOptions;
2567 // This is the internal entry point that code:EventListeners call when wanting to send a command to a
2568 // eventSource. The logic is as follows
2570 // * if Command == Update
2571 // * perEventSourceSessionId specifies the per-provider ETW session ID that the command applies
2572 // to (if listener != null)
2573 // perEventSourceSessionId = 0 - reserved for EventListeners
2574 // perEventSourceSessionId = 1..SessionMask.MAX - reserved for activity tracing aware ETW sessions
2575 // perEventSourceSessionId-1 represents the bit in the reserved field (bits 44..47) in
2576 // Keywords that identifies the session
2577 // perEventSourceSessionId = SessionMask.MAX+1 - reserved for legacy ETW sessions; these are
2578 // discriminated by etwSessionId
2579 // * etwSessionId specifies a machine-wide ETW session ID; this allows correlation of
2580 // activity tracing across different providers (which might have different sessionIds
2581 // for the same ETW session)
2582 // * enable, level, matchAnyKeywords are used to set a default for all events for the
2583 // eventSource. In particular, if 'enabled' is false, 'level' and
2584 // 'matchAnyKeywords' are not used.
2585 // * OnEventCommand is invoked, which may cause calls to
2586 // code:EventSource.EnableEventForDispatcher which may cause changes in the filtering
2587 // depending on the logic in that routine.
2588 // * else (command != Update)
2589 // * Simply call OnEventCommand. The expectation is that filtering is NOT changed.
2590 // * The 'enabled' 'level', matchAnyKeyword' arguments are ignored (must be true, 0, 0).
2592 // dispatcher == null has special meaning. It is the 'ETW' dispatcher.
2593 internal void SendCommand(EventListener listener, int perEventSourceSessionId, int etwSessionId,
2594 EventCommand command, bool enable,
2595 EventLevel level, EventKeywords matchAnyKeyword,
2596 IDictionary<string, string> commandArguments)
2598 var commandArgs = new EventCommandEventArgs(command, commandArguments, this, listener, perEventSourceSessionId, etwSessionId, enable, level, matchAnyKeyword);
2599 lock (EventListener.EventListenersLock)
2601 if (m_completelyInited)
2603 // After the first command arrive after construction, we are ready to get rid of the deferred commands
2604 this.m_deferredCommands = null;
2605 // We are fully initialized, do the command
2606 DoCommand(commandArgs);
2610 // We can't do the command, simply remember it and we do it when we are fully constructed.
2611 commandArgs.nextCommand = m_deferredCommands;
2612 m_deferredCommands = commandArgs;
2618 /// We want the eventSource to be fully initialized when we do commands because that way we can send
2619 /// error messages and other logging directly to the event stream. Unfortunately we can get callbacks
2620 /// when we are not fully initialized. In that case we store them in 'commandArgs' and do them later.
2621 /// This helper actually does all actual command logic.
2623 internal void DoCommand(EventCommandEventArgs commandArgs)
2625 // PRECONDITION: We should be holding the EventListener.EventListenersLock
2626 // We defer commands until we are completely inited. This allows error messages to be sent.
2627 Contract.Assert(m_completelyInited);
2629 #if FEATURE_MANAGED_ETW
2630 if (m_provider == null) // If we failed to construct
2632 #endif // FEATURE_MANAGED_ETW
2634 m_outOfBandMessageCount = 0;
2635 bool shouldReport = (commandArgs.perEventSourceSessionId > 0) && (commandArgs.perEventSourceSessionId <= SessionMask.MAX);
2638 EnsureDescriptorsInitialized();
2639 Contract.Assert(m_eventData != null);
2641 // Find the per-EventSource dispatcher corresponding to registered dispatcher
2642 commandArgs.dispatcher = GetDispatcher(commandArgs.listener);
2643 if (commandArgs.dispatcher == null && commandArgs.listener != null) // dispatcher == null means ETW dispatcher
2644 throw new ArgumentException(Environment.GetResourceString("EventSource_ListenerNotFound"));
2646 if (commandArgs.Arguments == null)
2647 commandArgs.Arguments = new Dictionary<string, string>();
2649 if (commandArgs.Command == EventCommand.Update)
2651 // Set it up using the 'standard' filtering bitfields (use the "global" enable, not session specific one)
2652 for (int i = 0; i < m_eventData.Length; i++)
2653 EnableEventForDispatcher(commandArgs.dispatcher, i, IsEnabledByDefault(i, commandArgs.enable, commandArgs.level, commandArgs.matchAnyKeyword));
2655 if (commandArgs.enable)
2657 if (!m_eventSourceEnabled)
2659 // EventSource turned on for the first time, simply copy the bits.
2660 m_level = commandArgs.level;
2661 m_matchAnyKeyword = commandArgs.matchAnyKeyword;
2665 // Already enabled, make it the most verbose of the existing and new filter
2666 if (commandArgs.level > m_level)
2667 m_level = commandArgs.level;
2668 if (commandArgs.matchAnyKeyword == 0)
2669 m_matchAnyKeyword = 0;
2670 else if (m_matchAnyKeyword != 0)
2671 m_matchAnyKeyword = unchecked(m_matchAnyKeyword | commandArgs.matchAnyKeyword);
2675 // interpret perEventSourceSessionId's sign, and adjust perEventSourceSessionId to
2676 // represent 0-based positive values
2677 bool bSessionEnable = (commandArgs.perEventSourceSessionId >= 0);
2678 if (commandArgs.perEventSourceSessionId == 0 && commandArgs.enable == false)
2679 bSessionEnable = false;
2681 if (commandArgs.listener == null)
2683 if (!bSessionEnable)
2684 commandArgs.perEventSourceSessionId = -commandArgs.perEventSourceSessionId;
2685 // for "global" enable/disable (passed in with listener == null and
2686 // perEventSourceSessionId == 0) perEventSourceSessionId becomes -1
2687 --commandArgs.perEventSourceSessionId;
2690 commandArgs.Command = bSessionEnable ? EventCommand.Enable : EventCommand.Disable;
2692 // perEventSourceSessionId = -1 when ETW sent a notification, but the set of active sessions
2694 // sesisonId = SessionMask.MAX when one of the legacy ETW sessions changed
2695 // 0 <= perEventSourceSessionId < SessionMask.MAX for activity-tracing aware sessions
2696 Contract.Assert(commandArgs.perEventSourceSessionId >= -1 && commandArgs.perEventSourceSessionId <= SessionMask.MAX);
2698 // Send the manifest if we are enabling an ETW session
2699 if (bSessionEnable && commandArgs.dispatcher == null)
2701 // eventSourceDispatcher == null means this is the ETW manifest
2703 // Note that we unconditionally send the manifest whenever we are enabled, even if
2704 // we were already enabled. This is because there may be multiple sessions active
2705 // and we can't know that all the sessions have seen the manifest.
2706 if (!SelfDescribingEvents)
2707 SendManifest(m_rawManifest);
2710 #if FEATURE_ACTIVITYSAMPLING
2711 if (bSessionEnable && commandArgs.perEventSourceSessionId != -1)
2713 bool participateInSampling = false;
2714 string activityFilters;
2717 ParseCommandArgs(commandArgs.Arguments, out participateInSampling,
2718 out activityFilters, out sessionIdBit);
2720 if (commandArgs.listener == null && commandArgs.Arguments.Count > 0 && commandArgs.perEventSourceSessionId != sessionIdBit)
2722 throw new ArgumentException(Environment.GetResourceString("EventSource_SessionIdError",
2723 commandArgs.perEventSourceSessionId + SessionMask.SHIFT_SESSION_TO_KEYWORD,
2724 sessionIdBit + SessionMask.SHIFT_SESSION_TO_KEYWORD));
2727 if (commandArgs.listener == null)
2729 UpdateEtwSession(commandArgs.perEventSourceSessionId, commandArgs.etwSessionId, true, activityFilters, participateInSampling);
2733 ActivityFilter.UpdateFilter(ref commandArgs.listener.m_activityFilter, this, 0, activityFilters);
2734 commandArgs.dispatcher.m_activityFilteringEnabled = participateInSampling;
2737 else if (!bSessionEnable && commandArgs.listener == null)
2739 // if we disable an ETW session, indicate that in a synthesized command argument
2740 if (commandArgs.perEventSourceSessionId >= 0 && commandArgs.perEventSourceSessionId < SessionMask.MAX)
2742 commandArgs.Arguments["EtwSessionKeyword"] = (commandArgs.perEventSourceSessionId + SessionMask.SHIFT_SESSION_TO_KEYWORD).ToString(CultureInfo.InvariantCulture);
2745 #endif // FEATURE_ACTIVITYSAMPLING
2747 // Turn on the enable bit before making the OnEventCommand callback This allows you to do useful
2748 // things like log messages, or test if keywords are enabled in the callback.
2749 if (commandArgs.enable)
2751 Contract.Assert(m_eventData != null);
2752 m_eventSourceEnabled = true;
2755 this.OnEventCommand(commandArgs);
2756 var eventCommandCallback = this.m_eventCommandExecuted;
2757 if (eventCommandCallback != null)
2758 eventCommandCallback(this, commandArgs);
2760 #if FEATURE_ACTIVITYSAMPLING
2761 if (commandArgs.listener == null && !bSessionEnable && commandArgs.perEventSourceSessionId != -1)
2763 // if we disable an ETW session, complete disabling it
2764 UpdateEtwSession(commandArgs.perEventSourceSessionId, commandArgs.etwSessionId, false, null, false);
2766 #endif // FEATURE_ACTIVITYSAMPLING
2768 if (!commandArgs.enable)
2770 // If we are disabling, maybe we can turn on 'quick checks' to filter
2771 // quickly. These are all just optimizations (since later checks will still filter)
2773 #if FEATURE_ACTIVITYSAMPLING
2774 // Turn off (and forget) any information about Activity Tracing.
2775 if (commandArgs.listener == null)
2777 // reset all filtering information for activity-tracing-aware sessions
2778 for (int i = 0; i < SessionMask.MAX; ++i)
2780 EtwSession etwSession = m_etwSessionIdMap[i];
2781 if (etwSession != null)
2782 ActivityFilter.DisableFilter(ref etwSession.m_activityFilter, this);
2784 m_activityFilteringForETWEnabled = new SessionMask(0);
2785 m_curLiveSessions = new SessionMask(0);
2786 // reset activity-tracing-aware sessions
2787 if (m_etwSessionIdMap != null)
2788 for (int i = 0; i < SessionMask.MAX; ++i)
2789 m_etwSessionIdMap[i] = null;
2790 // reset legacy sessions
2791 if (m_legacySessions != null)
2792 m_legacySessions.Clear();
2796 ActivityFilter.DisableFilter(ref commandArgs.listener.m_activityFilter, this);
2797 commandArgs.dispatcher.m_activityFilteringEnabled = false;
2799 #endif // FEATURE_ACTIVITYSAMPLING
2801 // There is a good chance EnabledForAnyListener are not as accurate as
2802 // they could be, go ahead and get a better estimate.
2803 for (int i = 0; i < m_eventData.Length; i++)
2805 bool isEnabledForAnyListener = false;
2806 for (EventDispatcher dispatcher = m_Dispatchers; dispatcher != null; dispatcher = dispatcher.m_Next)
2808 if (dispatcher.m_EventEnabled[i])
2810 isEnabledForAnyListener = true;
2814 m_eventData[i].EnabledForAnyListener = isEnabledForAnyListener;
2817 // If no events are enabled, disable the global enabled bit.
2818 if (!AnyEventEnabled())
2821 m_matchAnyKeyword = 0;
2822 m_eventSourceEnabled = false;
2825 #if FEATURE_ACTIVITYSAMPLING
2826 UpdateKwdTriggers(commandArgs.enable);
2827 #endif // FEATURE_ACTIVITYSAMPLING
2831 if (commandArgs.Command == EventCommand.SendManifest)
2833 // TODO: should we generate the manifest here if we hadn't already?
2834 if (m_rawManifest != null)
2835 SendManifest(m_rawManifest);
2838 // These are not used for non-update commands and thus should always be 'default' values
2839 // Contract.Assert(enable == true);
2840 // Contract.Assert(level == EventLevel.LogAlways);
2841 // Contract.Assert(matchAnyKeyword == EventKeywords.None);
2843 this.OnEventCommand(commandArgs);
2844 var eventCommandCallback = m_eventCommandExecuted;
2845 if (eventCommandCallback != null)
2846 eventCommandCallback(this, commandArgs);
2849 #if FEATURE_ACTIVITYSAMPLING
2850 if (m_completelyInited && (commandArgs.listener != null || shouldReport))
2852 SessionMask m = SessionMask.FromId(commandArgs.perEventSourceSessionId);
2853 ReportActivitySamplingInfo(commandArgs.listener, m);
2855 #endif // FEATURE_ACTIVITYSAMPLING
2859 // When the ETW session is created after the EventSource has registered with the ETW system
2860 // we can send any error messages here.
2861 ReportOutOfBandMessage("ERROR: Exception in Command Processing for EventSource " + Name + ": " + e.Message, true);
2862 // We never throw when doing a command.
2866 #if FEATURE_ACTIVITYSAMPLING
2868 internal void UpdateEtwSession(
2872 string activityFilters,
2873 bool participateInSampling)
2875 if (sessionIdBit < SessionMask.MAX)
2877 // activity-tracing-aware etw session
2880 var etwSession = EtwSession.GetEtwSession(etwSessionId, true);
2881 ActivityFilter.UpdateFilter(ref etwSession.m_activityFilter, this, sessionIdBit, activityFilters);
2882 m_etwSessionIdMap[sessionIdBit] = etwSession;
2883 m_activityFilteringForETWEnabled[sessionIdBit] = participateInSampling;
2887 var etwSession = EtwSession.GetEtwSession(etwSessionId);
2888 m_etwSessionIdMap[sessionIdBit] = null;
2889 m_activityFilteringForETWEnabled[sessionIdBit] = false;
2890 if (etwSession != null)
2892 ActivityFilter.DisableFilter(ref etwSession.m_activityFilter, this);
2893 // the ETW session is going away; remove it from the global list
2894 EtwSession.RemoveEtwSession(etwSession);
2897 m_curLiveSessions[sessionIdBit] = bEnable;
2901 // legacy etw session
2904 if (m_legacySessions == null)
2905 m_legacySessions = new List<EtwSession>(8);
2906 var etwSession = EtwSession.GetEtwSession(etwSessionId, true);
2907 if (!m_legacySessions.Contains(etwSession))
2908 m_legacySessions.Add(etwSession);
2912 var etwSession = EtwSession.GetEtwSession(etwSessionId);
2913 if (etwSession != null)
2915 if (m_legacySessions != null)
2916 m_legacySessions.Remove(etwSession);
2917 // the ETW session is going away; remove it from the global list
2918 EtwSession.RemoveEtwSession(etwSession);
2924 internal static bool ParseCommandArgs(
2925 IDictionary<string, string> commandArguments,
2926 out bool participateInSampling,
2927 out string activityFilters,
2928 out int sessionIdBit)
2931 participateInSampling = false;
2932 string activityFilterString;
2933 if (commandArguments.TryGetValue("ActivitySamplingStartEvent", out activityFilters))
2935 // if a start event is specified default the event source to participate in sampling
2936 participateInSampling = true;
2939 if (commandArguments.TryGetValue("ActivitySampling", out activityFilterString))
2941 if (string.Compare(activityFilterString, "false", StringComparison.OrdinalIgnoreCase) == 0 ||
2942 activityFilterString == "0")
2943 participateInSampling = false;
2945 participateInSampling = true;
2949 int sessionKwd = -1;
2950 if (!commandArguments.TryGetValue("EtwSessionKeyword", out sSessionKwd) ||
2951 !int.TryParse(sSessionKwd, out sessionKwd) ||
2952 sessionKwd < SessionMask.SHIFT_SESSION_TO_KEYWORD ||
2953 sessionKwd >= SessionMask.SHIFT_SESSION_TO_KEYWORD + SessionMask.MAX)
2960 sessionIdBit = sessionKwd - SessionMask.SHIFT_SESSION_TO_KEYWORD;
2965 internal void UpdateKwdTriggers(bool enable)
2969 // recompute m_keywordTriggers
2970 ulong gKeywords = unchecked((ulong)m_matchAnyKeyword);
2972 gKeywords = 0xFFFFffffFFFFffff;
2974 m_keywordTriggers = 0;
2975 for (int sessId = 0; sessId < SessionMask.MAX; ++sessId)
2977 EtwSession etwSession = m_etwSessionIdMap[sessId];
2978 if (etwSession == null)
2981 ActivityFilter activityFilter = etwSession.m_activityFilter;
2982 ActivityFilter.UpdateKwdTriggers(activityFilter, m_guid, this, unchecked((EventKeywords)gKeywords));
2987 m_keywordTriggers = 0;
2991 #endif // FEATURE_ACTIVITYSAMPLING
2994 /// If 'value is 'true' then set the eventSource so that 'dispatcher' will receive event with the eventId
2995 /// of 'eventId. If value is 'false' disable the event for that dispatcher. If 'eventId' is out of
2996 /// range return false, otherwise true.
2998 internal bool EnableEventForDispatcher(EventDispatcher dispatcher, int eventId, bool value)
3000 if (dispatcher == null)
3002 if (eventId >= m_eventData.Length)
3004 #if FEATURE_MANAGED_ETW
3005 if (m_provider != null)
3006 m_eventData[eventId].EnabledForETW = value;
3011 if (eventId >= dispatcher.m_EventEnabled.Length)
3013 dispatcher.m_EventEnabled[eventId] = value;
3015 m_eventData[eventId].EnabledForAnyListener = true;
3021 /// Returns true if any event at all is on.
3023 private bool AnyEventEnabled()
3025 for (int i = 0; i < m_eventData.Length; i++)
3026 if (m_eventData[i].EnabledForETW || m_eventData[i].EnabledForAnyListener)
3031 private bool IsDisposed
3033 #if FEATURE_MANAGED_ETW
3034 get { return m_provider == null || m_provider.m_disposed; }
3036 get { return false; } // ETW is not present (true means that the EventSource is "off" / broken)
3037 #endif // FEATURE_MANAGED_ETW
3040 [SecuritySafeCritical]
3041 private void EnsureDescriptorsInitialized()
3043 #if !ES_BUILD_STANDALONE
3044 Contract.Assert(Monitor.IsEntered(EventListener.EventListenersLock));
3046 if (m_eventData == null)
3048 Contract.Assert(m_rawManifest == null);
3049 m_rawManifest = CreateManifestAndDescriptors(this.GetType(), Name, this);
3050 Contract.Assert(m_eventData != null);
3052 // TODO Enforce singleton pattern
3053 foreach (WeakReference eventSourceRef in EventListener.s_EventSources)
3055 EventSource eventSource = eventSourceRef.Target as EventSource;
3056 if (eventSource != null && eventSource.Guid == m_guid && !eventSource.IsDisposed)
3058 if (eventSource != this)
3059 throw new ArgumentException(Environment.GetResourceString("EventSource_EventSourceGuidInUse", m_guid));
3063 // Make certain all dispatchers also have their arrays initialized
3064 EventDispatcher dispatcher = m_Dispatchers;
3065 while (dispatcher != null)
3067 if (dispatcher.m_EventEnabled == null)
3068 dispatcher.m_EventEnabled = new bool[m_eventData.Length];
3069 dispatcher = dispatcher.m_Next;
3072 if (s_currentPid == 0)
3074 #if ES_BUILD_STANDALONE && !ES_BUILD_PCL
3075 // for non-BCL EventSource we must assert SecurityPermission
3076 new SecurityPermission(PermissionState.Unrestricted).Assert();
3078 s_currentPid = Win32Native.GetCurrentProcessId();
3082 // Send out the ETW manifest XML out to ETW
3083 // Today, we only send the manifest to ETW, custom listeners don't get it.
3084 [SecuritySafeCritical]
3085 private unsafe bool SendManifest(byte[] rawManifest)
3087 bool success = true;
3089 if (rawManifest == null)
3092 Contract.Assert(!SelfDescribingEvents);
3094 #if FEATURE_MANAGED_ETW
3095 fixed (byte* dataPtr = rawManifest)
3097 // we don't want the manifest to show up in the event log channels so we specify as keywords
3098 // everything but the first 8 bits (reserved for the 8 channels)
3099 var manifestDescr = new EventDescriptor(0xFFFE, 1, 0, 0, 0xFE, 0xFFFE, 0x00ffFFFFffffFFFF);
3100 ManifestEnvelope envelope = new ManifestEnvelope();
3102 envelope.Format = ManifestEnvelope.ManifestFormats.SimpleXmlFormat;
3103 envelope.MajorVersion = 1;
3104 envelope.MinorVersion = 0;
3105 envelope.Magic = 0x5B; // An unusual number that can be checked for consistency.
3106 int dataLeft = rawManifest.Length;
3107 envelope.ChunkNumber = 0;
3109 EventProvider.EventData* dataDescrs = stackalloc EventProvider.EventData[2];
3110 dataDescrs[0].Ptr = (ulong)&envelope;
3111 dataDescrs[0].Size = (uint)sizeof(ManifestEnvelope);
3112 dataDescrs[0].Reserved = 0;
3114 dataDescrs[1].Ptr = (ulong)dataPtr;
3115 dataDescrs[1].Reserved = 0;
3117 int chunkSize = ManifestEnvelope.MaxChunkSize;
3118 TRY_AGAIN_WITH_SMALLER_CHUNK_SIZE:
3119 envelope.TotalChunks = (ushort)((dataLeft + (chunkSize - 1)) / chunkSize);
3120 while (dataLeft > 0)
3122 dataDescrs[1].Size = (uint)Math.Min(dataLeft, chunkSize);
3123 if (m_provider != null)
3125 if (!m_provider.WriteEvent(ref manifestDescr, null, null, 2, (IntPtr)dataDescrs))
3127 // Turns out that if users set the BufferSize to something less than 64K then WriteEvent
3128 // can fail. If we get this failure on the first chunk try again with something smaller
3129 // The smallest BufferSize is 1K so if we get to 256 (to account for envelope overhead), we can give up making it smaller.
3130 if (EventProvider.GetLastWriteEventError() == EventProvider.WriteEventErrorCode.EventTooBig)
3132 if (envelope.ChunkNumber == 0 && chunkSize > 256)
3134 chunkSize = chunkSize / 2;
3135 goto TRY_AGAIN_WITH_SMALLER_CHUNK_SIZE;
3139 if (ThrowOnEventWriteErrors)
3140 ThrowEventSourceException();
3144 dataLeft -= chunkSize;
3145 dataDescrs[1].Ptr += (uint)chunkSize;
3146 envelope.ChunkNumber++;
3149 #endif // FEATURE_MANAGED_ETW
3154 internal static Attribute GetCustomAttributeHelper(Type type, Type attributeType, EventManifestOptions flags = EventManifestOptions.None)
3156 return GetCustomAttributeHelper(type.GetTypeInfo(), attributeType, flags);
3160 // Helper to deal with the fact that the type we are reflecting over might be loaded in the ReflectionOnly context.
3161 // When that is the case, we have the build the custom assemblies on a member by hand.
3162 internal static Attribute GetCustomAttributeHelper(MemberInfo member, Type attributeType, EventManifestOptions flags = EventManifestOptions.None)
3164 if (!member.Module.Assembly.ReflectionOnly() && (flags & EventManifestOptions.AllowEventSourceOverride) == 0)
3166 // Let the runtime to the work for us, since we can execute code in this context.
3167 Attribute firstAttribute = null;
3168 foreach (var attribute in member.GetCustomAttributes(attributeType, false))
3170 firstAttribute = (Attribute)attribute;
3173 return firstAttribute;
3177 // In the reflection only context, we have to do things by hand.
3178 string fullTypeNameToFind = attributeType.FullName;
3180 #if EVENT_SOURCE_LEGACY_NAMESPACE_SUPPORT
3181 fullTypeNameToFind = fullTypeNameToFind.Replace("System.Diagnostics.Eventing", "System.Diagnostics.Tracing");
3184 foreach (CustomAttributeData data in CustomAttributeData.GetCustomAttributes(member))
3186 if (AttributeTypeNamesMatch(attributeType, data.Constructor.ReflectedType))
3188 Attribute attr = null;
3190 Contract.Assert(data.ConstructorArguments.Count <= 1);
3192 if (data.ConstructorArguments.Count == 1)
3194 attr = (Attribute)Activator.CreateInstance(attributeType, new object[] { data.ConstructorArguments[0].Value });
3196 else if (data.ConstructorArguments.Count == 0)
3198 attr = (Attribute)Activator.CreateInstance(attributeType);
3203 Type t = attr.GetType();
3205 foreach (CustomAttributeNamedArgument namedArgument in data.NamedArguments)
3207 PropertyInfo p = t.GetProperty(namedArgument.MemberInfo.Name, BindingFlags.Public | BindingFlags.Instance);
3208 object value = namedArgument.TypedValue.Value;
3210 if (p.PropertyType.IsEnum)
3212 value = Enum.Parse(p.PropertyType, value.ToString());
3215 p.SetValue(attr, value, null);
3224 #else // ES_BUILD_PCL
3225 throw new ArgumentException(Environment.GetResourceString("EventSource", "EventSource_PCLPlatformNotSupportedReflection"));
3230 /// Evaluates if two related "EventSource"-domain types should be considered the same
3232 /// <param name="attributeType">The attribute type in the load context - it's associated with the running
3233 /// EventSource type. This type may be different fromt he base type of the user-defined EventSource.</param>
3234 /// <param name="reflectedAttributeType">The attribute type in the reflection context - it's associated with
3235 /// the user-defined EventSource, and is in the same assembly as the eventSourceType passed to
3237 /// <returns>True - if the types should be considered equivalent, False - otherwise</returns>
3238 private static bool AttributeTypeNamesMatch(Type attributeType, Type reflectedAttributeType)
3241 // are these the same type?
3242 attributeType == reflectedAttributeType ||
3243 // are the full typenames equal?
3244 string.Equals(attributeType.FullName, reflectedAttributeType.FullName, StringComparison.Ordinal) ||
3245 // are the typenames equal and the namespaces under "Diagnostics.Tracing" (typically
3246 // either Microsoft.Diagnostics.Tracing or System.Diagnostics.Tracing)?
3247 string.Equals(attributeType.Name, reflectedAttributeType.Name, StringComparison.Ordinal) &&
3248 attributeType.Namespace.EndsWith("Diagnostics.Tracing") &&
3249 (reflectedAttributeType.Namespace.EndsWith("Diagnostics.Tracing")
3250 #if EVENT_SOURCE_LEGACY_NAMESPACE_SUPPORT
3251 || reflectedAttributeType.Namespace.EndsWith("Diagnostics.Eventing")
3256 private static Type GetEventSourceBaseType(Type eventSourceType, bool allowEventSourceOverride, bool reflectionOnly)
3258 // return false for "object" and interfaces
3259 if (eventSourceType.BaseType() == null)
3262 // now go up the inheritance chain until hitting a concrete type ("object" at worse)
3265 eventSourceType = eventSourceType.BaseType();
3267 while (eventSourceType != null && eventSourceType.IsAbstract());
3269 if (eventSourceType != null)
3271 if (!allowEventSourceOverride)
3273 if (reflectionOnly && eventSourceType.FullName != typeof(EventSource).FullName ||
3274 !reflectionOnly && eventSourceType != typeof(EventSource))
3279 if (eventSourceType.Name != "EventSource")
3283 return eventSourceType;
3286 // Use reflection to look at the attributes of a class, and generate a manifest for it (as UTF8) and
3287 // return the UTF8 bytes. It also sets up the code:EventData structures needed to dispatch events
3288 // at run time. 'source' is the event source to place the descriptors. If it is null,
3289 // then the descriptors are not creaed, and just the manifest is generated.
3290 private static byte[] CreateManifestAndDescriptors(Type eventSourceType, string eventSourceDllName, EventSource source,
3291 EventManifestOptions flags = EventManifestOptions.None)
3293 ManifestBuilder manifest = null;
3294 bool bNeedsManifest = source != null ? !source.SelfDescribingEvents : true;
3295 Exception exception = null; // exception that might get raised during validation b/c we couldn't/didn't recover from a previous error
3298 if (eventSourceType.IsAbstract() && (flags & EventManifestOptions.Strict) == 0)
3301 #if DEBUG && ES_BUILD_STANDALONE
3302 TestSupport.TestHooks.MaybeThrow(eventSourceType,
3303 TestSupport.Category.ManifestError,
3304 "EventSource_CreateManifestAndDescriptors",
3305 new ArgumentException("EventSource_CreateManifestAndDescriptors"));
3310 MethodInfo[] methods = eventSourceType.GetMethods(BindingFlags.DeclaredOnly | BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance);
3311 EventAttribute defaultEventAttribute;
3312 int eventId = 1; // The number given to an event that does not have a explicitly given ID.
3313 EventMetadata[] eventData = null;
3314 Dictionary<string, string> eventsByName = null;
3315 if (source != null || (flags & EventManifestOptions.Strict) != 0)
3317 eventData = new EventMetadata[methods.Length + 1];
3318 eventData[0].Name = ""; // Event 0 is the 'write messages string' event, and has an empty name.
3321 // See if we have localization information.
3322 ResourceManager resources = null;
3323 EventSourceAttribute eventSourceAttrib = (EventSourceAttribute)GetCustomAttributeHelper(eventSourceType, typeof(EventSourceAttribute), flags);
3324 if (eventSourceAttrib != null && eventSourceAttrib.LocalizationResources != null)
3325 resources = new ResourceManager(eventSourceAttrib.LocalizationResources, eventSourceType.Assembly());
3327 manifest = new ManifestBuilder(GetName(eventSourceType, flags), GetGuid(eventSourceType), eventSourceDllName,
3330 // Add an entry unconditionally for event ID 0 which will be for a string message.
3331 manifest.StartEvent("EventSourceMessage", new EventAttribute(0) { Level = EventLevel.LogAlways, Task = (EventTask)0xFFFE });
3332 manifest.AddEventParameter(typeof(string), "message");
3333 manifest.EndEvent();
3335 // eventSourceType must be sealed and must derive from this EventSource
3336 if ((flags & EventManifestOptions.Strict) != 0)
3338 bool typeMatch = GetEventSourceBaseType(eventSourceType, (flags & EventManifestOptions.AllowEventSourceOverride) != 0, eventSourceType.Assembly().ReflectionOnly()) != null;
3341 manifest.ManifestError(Environment.GetResourceString("EventSource_TypeMustDeriveFromEventSource"));
3342 if (!eventSourceType.IsAbstract() && !eventSourceType.IsSealed())
3343 manifest.ManifestError(Environment.GetResourceString("EventSource_TypeMustBeSealedOrAbstract"));
3346 // Collect task, opcode, keyword and channel information
3347 #if FEATURE_MANAGED_ETW_CHANNELS && FEATURE_ADVANCED_MANAGED_ETW_CHANNELS
3348 foreach (var providerEnumKind in new string[] { "Keywords", "Tasks", "Opcodes", "Channels" })
3350 foreach (var providerEnumKind in new string[] { "Keywords", "Tasks", "Opcodes" })
3353 Type nestedType = eventSourceType.GetNestedType(providerEnumKind);
3354 if (nestedType != null)
3356 if (eventSourceType.IsAbstract())
3358 manifest.ManifestError(Environment.GetResourceString("EventSource_AbstractMustNotDeclareKTOC", nestedType.Name));
3362 foreach (FieldInfo staticField in nestedType.GetFields(BindingFlags.DeclaredOnly | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static))
3364 AddProviderEnumKind(manifest, staticField, providerEnumKind);
3369 // ensure we have keywords for the session-filtering reserved bits
3371 manifest.AddKeyword("Session3", (long)0x1000 << 32);
3372 manifest.AddKeyword("Session2", (long)0x2000 << 32);
3373 manifest.AddKeyword("Session1", (long)0x4000 << 32);
3374 manifest.AddKeyword("Session0", (long)0x8000 << 32);
3377 if (eventSourceType != typeof(EventSource))
3379 for (int i = 0; i < methods.Length; i++)
3381 MethodInfo method = methods[i];
3382 ParameterInfo[] args = method.GetParameters();
3384 // Get the EventDescriptor (from the Custom attributes)
3385 EventAttribute eventAttribute = (EventAttribute)GetCustomAttributeHelper(method, typeof(EventAttribute), flags);
3387 // Compat: until v4.5.1 we ignored any non-void returning methods as well as virtual methods for
3388 // the only reason of limiting the number of methods considered to be events. This broke a common
3389 // design of having event sources implement specific interfaces. To fix this in a compatible way
3390 // we will now allow both non-void returning and virtual methods to be Event methods, as long
3391 // as they are marked with the [Event] attribute
3392 if (/* method.IsVirtual || */ method.IsStatic)
3397 if (eventSourceType.IsAbstract())
3399 if (eventAttribute != null)
3400 manifest.ManifestError(Environment.GetResourceString("EventSource_AbstractMustNotDeclareEventMethods", method.Name, eventAttribute.EventId));
3403 else if (eventAttribute == null)
3405 // Methods that don't return void can't be events, if they're NOT marked with [Event].
3406 // (see Compat comment above)
3407 if (method.ReturnType != typeof(void))
3412 // Continue to ignore virtual methods if they do NOT have the [Event] attribute
3413 // (see Compat comment above)
3414 if (method.IsVirtual)
3419 // If we explicitly mark the method as not being an event, then honor that.
3420 if (GetCustomAttributeHelper(method, typeof(NonEventAttribute), flags) != null)
3423 defaultEventAttribute = new EventAttribute(eventId);
3424 eventAttribute = defaultEventAttribute;
3426 else if (eventAttribute.EventId <= 0)
3428 manifest.ManifestError(Environment.GetResourceString("EventSource_NeedPositiveId", method.Name), true);
3429 continue; // don't validate anything else for this event
3431 if (method.Name.LastIndexOf('.') >= 0)
3432 manifest.ManifestError(Environment.GetResourceString("EventSource_EventMustNotBeExplicitImplementation", method.Name, eventAttribute.EventId));
3435 string eventName = method.Name;
3437 if (eventAttribute.Opcode == EventOpcode.Info) // We are still using the default opcode.
3439 // By default pick a task ID derived from the EventID, starting with the highest task number and working back
3440 bool noTask = (eventAttribute.Task == EventTask.None);
3442 eventAttribute.Task = (EventTask)(0xFFFE - eventAttribute.EventId);
3444 // Unless we explicitly set the opcode to Info (to override the auto-generate of Start or Stop opcodes,
3445 // pick a default opcode based on the event name (either Info or start or stop if the name ends with that suffix).
3446 if (!eventAttribute.IsOpcodeSet)
3447 eventAttribute.Opcode = GetOpcodeWithDefault(EventOpcode.Info, eventName);
3449 // Make the stop opcode have the same task as the start opcode.
3452 if (eventAttribute.Opcode == EventOpcode.Start)
3454 string taskName = eventName.Substring(0, eventName.Length - s_ActivityStartSuffix.Length); // Remove the Stop suffix to get the task name
3455 if (string.Compare(eventName, 0, taskName, 0, taskName.Length) == 0 &&
3456 string.Compare(eventName, taskName.Length, s_ActivityStartSuffix, 0, Math.Max(eventName.Length - taskName.Length, s_ActivityStartSuffix.Length)) == 0)
3458 // Add a task that is just the task name for the start event. This suppress the auto-task generation
3459 // That would otherwise happen (and create 'TaskName'Start as task name rather than just 'TaskName'
3460 manifest.AddTask(taskName, (int)eventAttribute.Task);
3463 else if (eventAttribute.Opcode == EventOpcode.Stop)
3465 // Find the start associated with this stop event. We require start to be immediately before the stop
3466 int startEventId = eventAttribute.EventId - 1;
3467 if (eventData != null && startEventId < eventData.Length)
3469 Contract.Assert(0 <= startEventId); // Since we reserve id 0, we know that id-1 is <= 0
3470 EventMetadata startEventMetadata = eventData[startEventId];
3472 // If you remove the Stop and add a Start does that name match the Start Event's Name?
3473 // Ideally we would throw an error
3474 string taskName = eventName.Substring(0, eventName.Length - s_ActivityStopSuffix.Length); // Remove the Stop suffix to get the task name
3475 if (startEventMetadata.Descriptor.Opcode == (byte)EventOpcode.Start &&
3476 string.Compare(startEventMetadata.Name, 0, taskName, 0, taskName.Length) == 0 &&
3477 string.Compare(startEventMetadata.Name, taskName.Length, s_ActivityStartSuffix, 0, Math.Max(startEventMetadata.Name.Length - taskName.Length, s_ActivityStartSuffix.Length)) == 0)
3480 // Make the stop event match the start event
3481 eventAttribute.Task = (EventTask)startEventMetadata.Descriptor.Task;
3485 if (noTask && (flags & EventManifestOptions.Strict) != 0) // Throw an error if we can compatibly.
3486 throw new ArgumentException(Environment.GetResourceString("EventSource_StopsFollowStarts"));
3491 bool hasRelatedActivityID = RemoveFirstArgIfRelatedActivityId(ref args);
3492 if (!(source != null && source.SelfDescribingEvents))
3494 manifest.StartEvent(eventName, eventAttribute);
3495 for (int fieldIdx = 0; fieldIdx < args.Length; fieldIdx++)
3497 manifest.AddEventParameter(args[fieldIdx].ParameterType, args[fieldIdx].Name);
3499 manifest.EndEvent();
3502 if (source != null || (flags & EventManifestOptions.Strict) != 0)
3504 // Do checking for user errors (optional, but not a big deal so we do it).
3505 DebugCheckEvent(ref eventsByName, eventData, method, eventAttribute, manifest, flags);
3507 #if FEATURE_MANAGED_ETW_CHANNELS
3508 // add the channel keyword for Event Viewer channel based filters. This is added for creating the EventDescriptors only
3509 // and is not required for the manifest
3510 if (eventAttribute.Channel != EventChannel.None)
3514 eventAttribute.Keywords |= (EventKeywords)manifest.GetChannelKeyword(eventAttribute.Channel);
3518 string eventKey = "event_" + eventName;
3519 string msg = manifest.GetLocalizedMessage(eventKey, CultureInfo.CurrentUICulture, etwFormat: false);
3520 // overwrite inline message with the localized message
3521 if (msg != null) eventAttribute.Message = msg;
3523 AddEventDescriptor(ref eventData, eventName, eventAttribute, args, hasRelatedActivityID);
3528 // Tell the TraceLogging stuff where to start allocating its own IDs.
3529 NameInfo.ReserveEventIDsBelow(eventId);
3533 TrimEventDescriptors(ref eventData);
3534 source.m_eventData = eventData; // officially initialize it. We do this at most once (it is racy otherwise).
3535 #if FEATURE_MANAGED_ETW_CHANNELS
3536 source.m_channelData = manifest.GetChannelData();
3540 // if this is an abstract event source we've already performed all the validation we can
3541 if (!eventSourceType.IsAbstract() && (source == null || !source.SelfDescribingEvents))
3543 bNeedsManifest = (flags & EventManifestOptions.OnlyIfNeededForRegistration) == 0
3544 #if FEATURE_MANAGED_ETW_CHANNELS
3545 || manifest.GetChannelData().Length > 0
3549 // if the manifest is not needed and we're not requested to validate the event source return early
3550 if (!bNeedsManifest && (flags & EventManifestOptions.Strict) == 0)
3553 res = manifest.CreateManifest();
3558 // if this is a runtime manifest generation let the exception propagate
3559 if ((flags & EventManifestOptions.Strict) == 0)
3561 // else store it to include it in the Argument exception we raise below
3565 if ((flags & EventManifestOptions.Strict) != 0 && (manifest.Errors.Count > 0 || exception != null))
3567 string msg = String.Empty;
3568 if (manifest.Errors.Count > 0)
3570 bool firstError = true;
3571 foreach (string error in manifest.Errors)
3574 msg += Environment.NewLine;
3580 msg = "Unexpected error: " + exception.Message;
3582 throw new ArgumentException(msg, exception);
3585 return bNeedsManifest ? res : null;
3588 private static bool RemoveFirstArgIfRelatedActivityId(ref ParameterInfo[] args)
3590 // If the first parameter is (case insensitive) 'relatedActivityId' then skip it.
3591 if (args.Length > 0 && args[0].ParameterType == typeof(Guid) &&
3592 string.Compare(args[0].Name, "relatedActivityId", StringComparison.OrdinalIgnoreCase) == 0)
3594 var newargs = new ParameterInfo[args.Length - 1];
3595 Array.Copy(args, 1, newargs, 0, args.Length - 1);
3604 // adds a enumeration (keyword, opcode, task or channel) represented by 'staticField'
3606 private static void AddProviderEnumKind(ManifestBuilder manifest, FieldInfo staticField, string providerEnumKind)
3608 bool reflectionOnly = staticField.Module.Assembly.ReflectionOnly();
3609 Type staticFieldType = staticField.FieldType;
3610 if (!reflectionOnly && (staticFieldType == typeof(EventOpcode)) || AttributeTypeNamesMatch(staticFieldType, typeof(EventOpcode)))
3612 if (providerEnumKind != "Opcodes") goto Error;
3613 int value = (int)staticField.GetRawConstantValue();
3614 manifest.AddOpcode(staticField.Name, value);
3616 else if (!reflectionOnly && (staticFieldType == typeof(EventTask)) || AttributeTypeNamesMatch(staticFieldType, typeof(EventTask)))
3618 if (providerEnumKind != "Tasks") goto Error;
3619 int value = (int)staticField.GetRawConstantValue();
3620 manifest.AddTask(staticField.Name, value);
3622 else if (!reflectionOnly && (staticFieldType == typeof(EventKeywords)) || AttributeTypeNamesMatch(staticFieldType, typeof(EventKeywords)))
3624 if (providerEnumKind != "Keywords") goto Error;
3625 ulong value = unchecked((ulong)(long)staticField.GetRawConstantValue());
3626 manifest.AddKeyword(staticField.Name, value);
3628 #if FEATURE_MANAGED_ETW_CHANNELS && FEATURE_ADVANCED_MANAGED_ETW_CHANNELS
3629 else if (!reflectionOnly && (staticFieldType == typeof(EventChannel)) || AttributeTypeNamesMatch(staticFieldType, typeof(EventChannel)))
3631 if (providerEnumKind != "Channels") goto Error;
3632 var channelAttribute = (EventChannelAttribute)GetCustomAttributeHelper(staticField, typeof(EventChannelAttribute));
3633 manifest.AddChannel(staticField.Name, (byte)staticField.GetRawConstantValue(), channelAttribute);
3638 manifest.ManifestError(Environment.GetResourceString("EventSource_EnumKindMismatch", staticField.Name, staticField.FieldType.Name, providerEnumKind));
3641 // Helper used by code:CreateManifestAndDescriptors to add a code:EventData descriptor for a method
3642 // with the code:EventAttribute 'eventAttribute'. resourceManger may be null in which case we populate it
3643 // it is populated if we need to look up message resources
3644 private static void AddEventDescriptor(ref EventMetadata[] eventData, string eventName,
3645 EventAttribute eventAttribute, ParameterInfo[] eventParameters,
3646 bool hasRelatedActivityID)
3648 if (eventData == null || eventData.Length <= eventAttribute.EventId)
3650 EventMetadata[] newValues = new EventMetadata[Math.Max(eventData.Length + 16, eventAttribute.EventId + 1)];
3651 Array.Copy(eventData, newValues, eventData.Length);
3652 eventData = newValues;
3655 eventData[eventAttribute.EventId].Descriptor = new EventDescriptor(
3656 eventAttribute.EventId,
3657 eventAttribute.Version,
3658 #if FEATURE_MANAGED_ETW_CHANNELS
3659 (byte)eventAttribute.Channel,
3663 (byte)eventAttribute.Level,
3664 (byte)eventAttribute.Opcode,
3665 (int)eventAttribute.Task,
3666 unchecked((long)((ulong)eventAttribute.Keywords | SessionMask.All.ToEventKeywords())));
3668 eventData[eventAttribute.EventId].Tags = eventAttribute.Tags;
3669 eventData[eventAttribute.EventId].Name = eventName;
3670 eventData[eventAttribute.EventId].Parameters = eventParameters;
3671 eventData[eventAttribute.EventId].Message = eventAttribute.Message;
3672 eventData[eventAttribute.EventId].ActivityOptions = eventAttribute.ActivityOptions;
3673 eventData[eventAttribute.EventId].HasRelatedActivityID = hasRelatedActivityID;
3676 // Helper used by code:CreateManifestAndDescriptors that trims the m_eventData array to the correct
3677 // size after all event descriptors have been added.
3678 private static void TrimEventDescriptors(ref EventMetadata[] eventData)
3680 int idx = eventData.Length;
3684 if (eventData[idx].Descriptor.EventId != 0)
3687 if (eventData.Length - idx > 2) // allow one wasted slot.
3689 EventMetadata[] newValues = new EventMetadata[idx + 1];
3690 Array.Copy(eventData, newValues, newValues.Length);
3691 eventData = newValues;
3695 // Helper used by code:EventListener.AddEventSource and code:EventListener.EventListener
3696 // when a listener gets attached to a eventSource
3697 internal void AddListener(EventListener listener)
3699 lock (EventListener.EventListenersLock)
3701 bool[] enabledArray = null;
3702 if (m_eventData != null)
3703 enabledArray = new bool[m_eventData.Length];
3704 m_Dispatchers = new EventDispatcher(m_Dispatchers, enabledArray, listener);
3705 listener.OnEventSourceCreated(this);
3709 // Helper used by code:CreateManifestAndDescriptors to find user mistakes like reusing an event
3710 // index for two distinct events etc. Throws exceptions when it finds something wrong.
3711 private static void DebugCheckEvent(ref Dictionary<string, string> eventsByName,
3712 EventMetadata[] eventData, MethodInfo method, EventAttribute eventAttribute,
3713 ManifestBuilder manifest, EventManifestOptions options)
3715 int evtId = eventAttribute.EventId;
3716 string evtName = method.Name;
3717 int eventArg = GetHelperCallFirstArg(method);
3718 if (eventArg >= 0 && evtId != eventArg)
3720 manifest.ManifestError(Environment.GetResourceString("EventSource_MismatchIdToWriteEvent", evtName, evtId, eventArg), true);
3723 if (evtId < eventData.Length && eventData[evtId].Descriptor.EventId != 0)
3725 manifest.ManifestError(Environment.GetResourceString("EventSource_EventIdReused", evtName, evtId, eventData[evtId].Name), true);
3728 // We give a task to things if they don't have one.
3729 // TODO this is moderately expensive (N*N). We probably should not even bother....
3730 Contract.Assert(eventAttribute.Task != EventTask.None || eventAttribute.Opcode != EventOpcode.Info);
3731 for (int idx = 0; idx < eventData.Length; ++idx)
3733 // skip unused Event IDs.
3734 if (eventData[idx].Name == null)
3737 if (eventData[idx].Descriptor.Task == (int)eventAttribute.Task && eventData[idx].Descriptor.Opcode == (int)eventAttribute.Opcode)
3739 manifest.ManifestError(Environment.GetResourceString("EventSource_TaskOpcodePairReused",
3740 evtName, evtId, eventData[idx].Name, idx));
3742 // If we are not strict stop on first error. We have had problems with really large providers taking forever. because of many errors.
3743 if ((options & EventManifestOptions.Strict) == 0)
3748 // for non-default event opcodes the user must define a task!
3749 if (eventAttribute.Opcode != EventOpcode.Info)
3751 bool failure = false;
3752 if (eventAttribute.Task == EventTask.None)
3756 // If you have the auto-assigned Task, then you did not explicitly set one.
3757 // This is OK for Start events because we have special logic to assign the task to a prefix derived from the event name
3758 // But all other cases we want to catch the omission.
3759 var autoAssignedTask = (EventTask)(0xFFFE - evtId);
3760 if ((eventAttribute.Opcode != EventOpcode.Start && eventAttribute.Opcode != EventOpcode.Stop) && eventAttribute.Task == autoAssignedTask)
3764 manifest.ManifestError(Environment.GetResourceString("EventSource_EventMustHaveTaskIfNonDefaultOpcode", evtName, evtId));
3767 // If we ever want to enforce the rule: MethodName = TaskName + OpcodeName here's how:
3768 // (the reason we don't is backwards compat and the need for handling this as a non-fatal error
3769 // by eventRegister.exe)
3770 // taskName & opcodeName could be passed in by the caller which has opTab & taskTab handy
3771 // if (!(((int)eventAttribute.Opcode == 0 && evtName == taskName) || (evtName == taskName+opcodeName)))
3773 // throw new WarningException(Environment.GetResourceString("EventSource_EventNameDoesNotEqualTaskPlusOpcode"));
3776 if (eventsByName == null)
3777 eventsByName = new Dictionary<string, string>();
3779 if (eventsByName.ContainsKey(evtName))
3780 manifest.ManifestError(Environment.GetResourceString("EventSource_EventNameReused", evtName), true);
3782 eventsByName[evtName] = evtName;
3786 /// This method looks at the IL and tries to pattern match against the standard
3787 /// 'boilerplate' event body
3789 /// { if (Enabled()) WriteEvent(#, ...) }
3791 /// If the pattern matches, it returns the literal number passed as the first parameter to
3792 /// the WriteEvent. This is used to find common user errors (mismatching this
3793 /// number with the EventAttribute ID). It is only used for validation.
3795 /// <param name="method">The method to probe.</param>
3796 /// <returns>The literal value or -1 if the value could not be determined. </returns>
3797 [SecuritySafeCritical]
3798 [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Maintainability", "CA1502:AvoidExcessiveComplexity", Justification = "Switch statement is clearer than alternatives")]
3799 static private int GetHelperCallFirstArg(MethodInfo method)
3802 // Currently searches for the following pattern
3804 // ... // CAN ONLY BE THE INSTRUCTIONS BELOW
3807 // ... // CAN ONLY BE THE INSTRUCTIONS BELOW CAN'T BE A BRANCH OR A CALL
3809 // NOP // 0 or more times
3812 // If we find this pattern we return the XXX. Otherwise we return -1.
3813 (new ReflectionPermission(ReflectionPermissionFlag.MemberAccess)).Assert();
3814 byte[] instrs = method.GetMethodBody().GetILAsByteArray();
3816 for (int idx = 0; idx < instrs.Length; )
3818 switch (instrs[idx])
3841 case 21: // LDC_I4_M1
3842 case 22: // LDC_I4_0
3843 case 23: // LDC_I4_1
3844 case 24: // LDC_I4_2
3845 case 25: // LDC_I4_3
3846 case 26: // LDC_I4_4
3847 case 27: // LDC_I4_5
3848 case 28: // LDC_I4_6
3849 case 29: // LDC_I4_7
3850 case 30: // LDC_I4_8
3851 if (idx > 0 && instrs[idx - 1] == 2) // preceeded by LDARG0
3852 retVal = instrs[idx] - 22;
3854 case 31: // LDC_I4_S
3855 if (idx > 0 && instrs[idx - 1] == 2) // preceeded by LDARG0
3856 retVal = instrs[idx + 1];
3869 // Is this call just before return?
3870 for (int search = idx + 1; search < instrs.Length; search++)
3872 if (instrs[search] == 42) // RET
3874 if (instrs[search] != 0) // NOP
3880 case 44: // BRFALSE_S
3881 case 45: // BRTRUE_S
3890 case 103: // CONV_I1
3891 case 104: // CONV_I2
3892 case 105: // CONV_I4
3893 case 106: // CONV_I8
3894 case 109: // CONV_U4
3895 case 110: // CONV_U8
3901 case 162: // STELEM_REF
3905 // Covers the CEQ instructions used in debug code for some reason.
3906 if (idx >= instrs.Length || instrs[idx] >= 6)
3910 /* Contract.Assert(false, "Warning: User validation code sub-optimial: Unsuported opcode " + instrs[idx] +
3911 " at " + idx + " in method " + method.Name); */
3920 #if false // This routine is not needed at all, it was used for unit test debugging.
3921 [Conditional("DEBUG")]
3922 private static void OutputDebugString(string msg)
3925 msg = msg.TrimEnd('\r', '\n') +
3926 string.Format(CultureInfo.InvariantCulture, ", Thrd({0})" + Environment.NewLine, Thread.CurrentThread.ManagedThreadId);
3927 System.Diagnostics.Debugger.Log(0, null, msg);
3933 /// Sends an error message to the debugger (outputDebugString), as well as the EventListeners
3934 /// It will do this even if the EventSource is not enabled.
3935 /// TODO remove flush parameter it is not used.
3937 [SuppressMessage("Microsoft.Concurrency", "CA8001", Justification = "This does not need to be correct when racing with other threads")]
3938 internal void ReportOutOfBandMessage(string msg, bool flush)
3943 // send message to debugger without delay
3944 System.Diagnostics.Debugger.Log(0, null, msg + "\r\n");
3947 // Send it to all listeners.
3948 if (m_outOfBandMessageCount < 16 - 1) // Note this is only if size byte
3949 m_outOfBandMessageCount++;
3952 if (m_outOfBandMessageCount == 16)
3954 m_outOfBandMessageCount = 16; // Mark that we hit the limit. Notify them that this is the case.
3955 msg = "Reached message limit. End of EventSource error messages.";
3958 WriteEventString(EventLevel.LogAlways, -1, msg);
3959 WriteStringToAllListeners("EventSourceMessage", msg);
3961 catch (Exception) { } // If we fail during last chance logging, well, we have to give up....
3964 private EventSourceSettings ValidateSettings(EventSourceSettings settings)
3966 var evtFormatMask = EventSourceSettings.EtwManifestEventFormat |
3967 EventSourceSettings.EtwSelfDescribingEventFormat;
3968 if ((settings & evtFormatMask) == evtFormatMask)
3969 throw new ArgumentException(Environment.GetResourceString("EventSource_InvalidEventFormat"), "settings");
3971 // If you did not explicitly ask for manifest, you get self-describing.
3972 if ((settings & evtFormatMask) == 0)
3973 settings |= EventSourceSettings.EtwSelfDescribingEventFormat;
3977 private bool ThrowOnEventWriteErrors
3979 get { return (m_config & EventSourceSettings.ThrowOnEventWriteErrors) != 0; }
3982 if (value) m_config |= EventSourceSettings.ThrowOnEventWriteErrors;
3983 else m_config &= ~EventSourceSettings.ThrowOnEventWriteErrors;
3987 private bool SelfDescribingEvents
3991 Contract.Assert(((m_config & EventSourceSettings.EtwManifestEventFormat) != 0) !=
3992 ((m_config & EventSourceSettings.EtwSelfDescribingEventFormat) != 0));
3993 return (m_config & EventSourceSettings.EtwSelfDescribingEventFormat) != 0;
3999 m_config |= EventSourceSettings.EtwManifestEventFormat;
4000 m_config &= ~EventSourceSettings.EtwSelfDescribingEventFormat;
4004 m_config |= EventSourceSettings.EtwSelfDescribingEventFormat;
4005 m_config &= ~EventSourceSettings.EtwManifestEventFormat;
4010 #if FEATURE_ACTIVITYSAMPLING
4011 private void ReportActivitySamplingInfo(EventListener listener, SessionMask sessions)
4013 Contract.Assert(listener == null || (uint)sessions == (uint)SessionMask.FromId(0));
4015 for (int perEventSourceSessionId = 0; perEventSourceSessionId < SessionMask.MAX; ++perEventSourceSessionId)
4017 if (!sessions[perEventSourceSessionId])
4021 if (listener == null)
4023 EtwSession etwSession = m_etwSessionIdMap[perEventSourceSessionId];
4024 Contract.Assert(etwSession != null);
4025 af = etwSession.m_activityFilter;
4029 af = listener.m_activityFilter;
4035 SessionMask m = new SessionMask();
4036 m[perEventSourceSessionId] = true;
4038 foreach (var t in af.GetFilterAsTuple(m_guid))
4040 WriteStringToListener(listener, string.Format(CultureInfo.InvariantCulture, "Session {0}: {1} = {2}", perEventSourceSessionId, t.Item1, t.Item2), m);
4043 bool participateInSampling = (listener == null) ?
4044 m_activityFilteringForETWEnabled[perEventSourceSessionId] :
4045 GetDispatcher(listener).m_activityFilteringEnabled;
4046 WriteStringToListener(listener, string.Format(CultureInfo.InvariantCulture, "Session {0}: Activity Sampling support: {1}",
4047 perEventSourceSessionId, participateInSampling ? "enabled" : "disabled"), m);
4050 #endif // FEATURE_ACTIVITYSAMPLING
4052 // private instance state
4053 private string m_name; // My friendly name (privided in ctor)
4054 internal int m_id; // A small integer that is unique to this instance.
4055 private Guid m_guid; // GUID representing the ETW eventSource to the OS.
4056 internal volatile EventMetadata[] m_eventData; // None per-event data
4057 private volatile byte[] m_rawManifest; // Bytes to send out representing the event schema
4059 private EventHandler<EventCommandEventArgs> m_eventCommandExecuted;
4061 private EventSourceSettings m_config; // configuration information
4064 private bool m_eventSourceEnabled; // am I enabled (any of my events are enabled for any dispatcher)
4065 internal EventLevel m_level; // highest level enabled by any output dispatcher
4066 internal EventKeywords m_matchAnyKeyword; // the logical OR of all levels enabled by any output dispatcher (zero is a special case) meaning 'all keywords'
4068 // Dispatching state
4069 internal volatile EventDispatcher m_Dispatchers; // Linked list of code:EventDispatchers we write the data to (we also do ETW specially)
4070 #if FEATURE_MANAGED_ETW
4071 private volatile OverideEventProvider m_provider; // This hooks up ETW commands to our 'OnEventCommand' callback
4073 private bool m_completelyInited; // The EventSource constructor has returned without exception.
4074 private Exception m_constructionException; // If there was an exception construction, this is it
4075 private byte m_outOfBandMessageCount; // The number of out of band messages sent (we throttle them
4076 private EventCommandEventArgs m_deferredCommands;// If we get commands before we are fully we store them here and run the when we are fully inited.
4078 private string[] m_traits; // Used to implement GetTraits
4080 internal static uint s_currentPid; // current process id, used in synthesizing quasi-GUIDs
4082 private static byte m_EventSourceExceptionRecurenceCount = 0; // current recursion count inside ThrowEventSourceException
4084 #if FEATURE_MANAGED_ETW_CHANNELS
4085 internal volatile ulong[] m_channelData;
4088 #if FEATURE_ACTIVITYSAMPLING
4089 private SessionMask m_curLiveSessions; // the activity-tracing aware sessions' bits
4090 private EtwSession[] m_etwSessionIdMap; // the activity-tracing aware sessions
4091 private List<EtwSession> m_legacySessions; // the legacy ETW sessions listening to this source
4092 internal long m_keywordTriggers; // a bit is set if it corresponds to a keyword that's part of an enabled triggering event
4093 internal SessionMask m_activityFilteringForETWEnabled; // does THIS EventSource have activity filtering turned on for each ETW session
4094 static internal Action<Guid> s_activityDying; // Fires when something calls SetCurrentThreadToActivity()
4095 // Also used to mark that activity tracing is on for some case
4096 #endif // FEATURE_ACTIVITYSAMPLING
4098 // We use a single instance of ActivityTracker for all EventSources instances to allow correlation between multiple event providers.
4099 // We have m_activityTracker field simply because instance field is more efficient than static field fetch.
4100 ActivityTracker m_activityTracker;
4101 internal const string s_ActivityStartSuffix = "Start";
4102 internal const string s_ActivityStopSuffix = "Stop";
4104 // used for generating GUID from eventsource name
4105 private static readonly byte[] namespaceBytes = new byte[] {
4106 0x48, 0x2C, 0x2D, 0xB2, 0xC3, 0x90, 0x47, 0xC8,
4107 0x87, 0xF8, 0x1A, 0x15, 0xBF, 0xC1, 0x30, 0xFB,
4114 /// Enables specifying event source configuration options to be used in the EventSource constructor.
4117 public enum EventSourceSettings
4120 /// This specifies none of the special configuration options should be enabled.
4124 /// Normally an EventSource NEVER throws; setting this option will tell it to throw when it encounters errors.
4126 ThrowOnEventWriteErrors = 1,
4128 /// Setting this option is a directive to the ETW listener should use manifest-based format when
4129 /// firing events. This is the default option when defining a type derived from EventSource
4130 /// (using the protected EventSource constructors).
4131 /// Only one of EtwManifestEventFormat or EtwSelfDescribingEventFormat should be specified
4133 EtwManifestEventFormat = 4,
4135 /// Setting this option is a directive to the ETW listener should use self-describing event format
4136 /// when firing events. This is the default option when creating a new instance of the EventSource
4137 /// type (using the public EventSource constructors).
4138 /// Only one of EtwManifestEventFormat or EtwSelfDescribingEventFormat should be specified
4140 EtwSelfDescribingEventFormat = 8,
4144 /// An EventListener represents a target for the events generated by EventSources (that is subclasses
4145 /// of <see cref="EventSource"/>), in the current appdomain. When a new EventListener is created
4146 /// it is logically attached to all eventSources in that appdomain. When the EventListener is Disposed, then
4147 /// it is disconnected from the event eventSources. Note that there is a internal list of STRONG references
4148 /// to EventListeners, which means that relying on the lack of references to EventListeners to clean up
4149 /// EventListeners will NOT work. You must call EventListener.Dispose explicitly when a dispatcher is no
4152 /// Once created, EventListeners can enable or disable on a per-eventSource basis using verbosity levels
4153 /// (<see cref="EventLevel"/>) and bitfields (<see cref="EventKeywords"/>) to further restrict the set of
4154 /// events to be sent to the dispatcher. The dispatcher can also send arbitrary commands to a particular
4155 /// eventSource using the 'SendCommand' method. The meaning of the commands are eventSource specific.
4157 /// The Null Guid (that is (new Guid()) has special meaning as a wildcard for 'all current eventSources in
4158 /// the appdomain'. Thus it is relatively easy to turn on all events in the appdomain if desired.
4160 /// It is possible for there to be many EventListener's defined in a single appdomain. Each dispatcher is
4161 /// logically independent of the other listeners. Thus when one dispatcher enables or disables events, it
4162 /// affects only that dispatcher (other listeners get the events they asked for). It is possible that
4163 /// commands sent with 'SendCommand' would do a semantic operation that would affect the other listeners
4164 /// (like doing a GC, or flushing data ...), but this is the exception rather than the rule.
4166 /// Thus the model is that each EventSource keeps a list of EventListeners that it is sending events
4167 /// to. Associated with each EventSource-dispatcher pair is a set of filtering criteria that determine for
4168 /// that eventSource what events that dispatcher will receive.
4170 /// Listeners receive the events on their 'OnEventWritten' method. Thus subclasses of EventListener must
4171 /// override this method to do something useful with the data.
4173 /// In addition, when new eventSources are created, the 'OnEventSourceCreate' method is called. The
4174 /// invariant associated with this callback is that every eventSource gets exactly one
4175 /// 'OnEventSourceCreate' call for ever eventSource that can potentially send it log messages. In
4176 /// particular when a EventListener is created, typically a series of OnEventSourceCreate' calls are
4177 /// made to notify the new dispatcher of all the eventSources that existed before the EventListener was
4181 public abstract class EventListener : IDisposable
4183 private event EventHandler<EventSourceCreatedEventArgs> _EventSourceCreated;
4186 /// This event is raised whenever a new eventSource is 'attached' to the dispatcher.
4187 /// This can happen for all existing EventSources when the EventListener is created
4188 /// as well as for any EventSources that come into existence after the EventListener
4189 /// has been created.
4191 /// These 'catch up' events are called during the construction of the EventListener.
4192 /// Subclasses need to be prepared for that.
4194 /// In a multi-threaded environment, it is possible that 'EventSourceEventWrittenCallback'
4195 /// events for a particular eventSource to occur BEFORE the EventSourceCreatedCallback is issued.
4197 public event EventHandler<EventSourceCreatedEventArgs> EventSourceCreated
4201 CallBackForExistingEventSources(false, value);
4203 this._EventSourceCreated = (EventHandler<EventSourceCreatedEventArgs>)Delegate.Combine(_EventSourceCreated, value);
4207 this._EventSourceCreated = (EventHandler<EventSourceCreatedEventArgs>)Delegate.Remove(_EventSourceCreated, value);
4212 /// This event is raised whenever an event has been written by a EventSource for which
4213 /// the EventListener has enabled events.
4215 public event EventHandler<EventWrittenEventArgs> EventWritten;
4218 /// Create a new EventListener in which all events start off turned off (use EnableEvents to turn
4221 protected EventListener()
4223 // This will cause the OnEventSourceCreated callback to fire.
4224 CallBackForExistingEventSources(true, (obj, args) => args.EventSource.AddListener(this) );
4228 /// Dispose should be called when the EventListener no longer desires 'OnEvent*' callbacks. Because
4229 /// there is an internal list of strong references to all EventListeners, calling 'Dispose' directly
4230 /// is the only way to actually make the listen die. Thus it is important that users of EventListener
4231 /// call Dispose when they are done with their logging.
4233 #if ES_BUILD_STANDALONE
4234 [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1063:ImplementIDisposableCorrectly")]
4236 public virtual void Dispose()
4238 lock (EventListenersLock)
4240 Contract.Assert(s_Listeners != null);
4241 if (s_Listeners != null)
4243 if (this == s_Listeners)
4245 EventListener cur = s_Listeners;
4246 s_Listeners = this.m_Next;
4247 RemoveReferencesToListenerInEventSources(cur);
4251 // Find 'this' from the s_Listeners linked list.
4252 EventListener prev = s_Listeners;
4255 EventListener cur = prev.m_Next;
4260 // Found our Listener, remove references to to it in the eventSources
4261 prev.m_Next = cur.m_Next; // Remove entry.
4262 RemoveReferencesToListenerInEventSources(cur);
4272 // We don't expose a Dispose(bool), because the contract is that you don't have any non-syncronous
4273 // 'cleanup' associated with this object
4276 /// Enable all events from the eventSource identified by 'eventSource' to the current
4277 /// dispatcher that have a verbosity level of 'level' or lower.
4279 /// This call can have the effect of REDUCING the number of events sent to the
4280 /// dispatcher if 'level' indicates a less verbose level than was previously enabled.
4282 /// This call never has an effect on other EventListeners.
4285 public void EnableEvents(EventSource eventSource, EventLevel level)
4287 EnableEvents(eventSource, level, EventKeywords.None);
4290 /// Enable all events from the eventSource identified by 'eventSource' to the current
4291 /// dispatcher that have a verbosity level of 'level' or lower and have a event keyword
4292 /// matching any of the bits in 'matchAnyKeyword'.
4294 /// This call can have the effect of REDUCING the number of events sent to the
4295 /// dispatcher if 'level' indicates a less verbose level than was previously enabled or
4296 /// if 'matchAnyKeyword' has fewer keywords set than where previously set.
4298 /// This call never has an effect on other EventListeners.
4300 public void EnableEvents(EventSource eventSource, EventLevel level, EventKeywords matchAnyKeyword)
4302 EnableEvents(eventSource, level, matchAnyKeyword, null);
4305 /// Enable all events from the eventSource identified by 'eventSource' to the current
4306 /// dispatcher that have a verbosity level of 'level' or lower and have a event keyword
4307 /// matching any of the bits in 'matchAnyKeyword' as well as any (eventSource specific)
4308 /// effect passing additional 'key-value' arguments 'arguments' might have.
4310 /// This call can have the effect of REDUCING the number of events sent to the
4311 /// dispatcher if 'level' indicates a less verbose level than was previously enabled or
4312 /// if 'matchAnyKeyword' has fewer keywords set than where previously set.
4314 /// This call never has an effect on other EventListeners.
4316 public void EnableEvents(EventSource eventSource, EventLevel level, EventKeywords matchAnyKeyword, IDictionary<string, string> arguments)
4318 if (eventSource == null)
4320 throw new ArgumentNullException("eventSource");
4322 Contract.EndContractBlock();
4324 eventSource.SendCommand(this, 0, 0, EventCommand.Update, true, level, matchAnyKeyword, arguments);
4327 /// Disables all events coming from eventSource identified by 'eventSource'.
4329 /// This call never has an effect on other EventListeners.
4331 public void DisableEvents(EventSource eventSource)
4333 if (eventSource == null)
4335 throw new ArgumentNullException("eventSource");
4337 Contract.EndContractBlock();
4339 eventSource.SendCommand(this, 0, 0, EventCommand.Update, false, EventLevel.LogAlways, EventKeywords.None, null);
4343 /// EventSourceIndex is small non-negative integer (suitable for indexing in an array)
4344 /// identifying EventSource. It is unique per-appdomain. Some EventListeners might find
4345 /// it useful to store additional information about each eventSource connected to it,
4346 /// and EventSourceIndex allows this extra information to be efficiently stored in a
4347 /// (growable) array (eg List(T)).
4349 public static int EventSourceIndex(EventSource eventSource) { return eventSource.m_id; }
4352 /// This method is called whenever a new eventSource is 'attached' to the dispatcher.
4353 /// This can happen for all existing EventSources when the EventListener is created
4354 /// as well as for any EventSources that come into existence after the EventListener
4355 /// has been created.
4357 /// These 'catch up' events are called during the construction of the EventListener.
4358 /// Subclasses need to be prepared for that.
4360 /// In a multi-threaded environment, it is possible that 'OnEventWritten' callbacks
4361 /// for a particular eventSource to occur BEFORE the OnEventSourceCreated is issued.
4363 /// <param name="eventSource"></param>
4364 internal protected virtual void OnEventSourceCreated(EventSource eventSource)
4366 EventHandler<EventSourceCreatedEventArgs> callBack = this._EventSourceCreated;
4367 if(callBack != null)
4369 EventSourceCreatedEventArgs args = new EventSourceCreatedEventArgs();
4370 args.EventSource = eventSource;
4371 callBack(this, args);
4376 /// This method is called whenever an event has been written by a EventSource for which
4377 /// the EventListener has enabled events.
4379 /// <param name="eventData"></param>
4380 internal protected virtual void OnEventWritten(EventWrittenEventArgs eventData)
4382 EventHandler<EventWrittenEventArgs> callBack = this.EventWritten;
4383 if (callBack != null)
4385 callBack(this, eventData);
4392 /// This routine adds newEventSource to the global list of eventSources, it also assigns the
4393 /// ID to the eventSource (which is simply the ordinal in the global list).
4395 /// EventSources currently do not pro-actively remove themselves from this list. Instead
4396 /// when eventSources's are GCed, the weak handle in this list naturally gets nulled, and
4397 /// we will reuse the slot. Today this list never shrinks (but we do reuse entries
4398 /// that are in the list). This seems OK since the expectation is that EventSources
4399 /// tend to live for the lifetime of the appdomain anyway (they tend to be used in
4400 /// global variables).
4402 /// <param name="newEventSource"></param>
4403 internal static void AddEventSource(EventSource newEventSource)
4405 lock (EventListenersLock)
4407 if (s_EventSources == null)
4408 s_EventSources = new List<WeakReference>(2);
4410 if (!s_EventSourceShutdownRegistered)
4412 s_EventSourceShutdownRegistered = true;
4413 #if !ES_BUILD_PCL && !FEATURE_CORECLR
4414 AppDomain.CurrentDomain.ProcessExit += DisposeOnShutdown;
4415 AppDomain.CurrentDomain.DomainUnload += DisposeOnShutdown;
4420 // Periodically search the list for existing entries to reuse, this avoids
4421 // unbounded memory use if we keep recycling eventSources (an unlikely thing).
4423 if (s_EventSources.Count % 64 == 63) // on every block of 64, fill up the block before continuing
4425 int i = s_EventSources.Count; // Work from the top down.
4429 WeakReference weakRef = s_EventSources[i];
4430 if (!weakRef.IsAlive)
4433 weakRef.Target = newEventSource;
4440 newIndex = s_EventSources.Count;
4441 s_EventSources.Add(new WeakReference(newEventSource));
4443 newEventSource.m_id = newIndex;
4445 // Add every existing dispatcher to the new EventSource
4446 for (EventListener listener = s_Listeners; listener != null; listener = listener.m_Next)
4447 newEventSource.AddListener(listener);
4453 // Whenver we have async callbacks from native code, there is an ugly issue where
4454 // during .NET shutdown native code could be calling the callback, but the CLR
4455 // has already prohibited callbacks to managed code in the appdomain, causing the CLR
4456 // to throw a COMPLUS_BOOT_EXCEPTION. The guideline we give is that you must unregister
4457 // such callbacks on process shutdown or appdomain so that unmanaged code will never
4458 // do this. This is what this callback is for.
4459 // See bug 724140 for more
4460 private static void DisposeOnShutdown(object sender, EventArgs e)
4462 lock(EventListenersLock)
4464 foreach (var esRef in s_EventSources)
4466 EventSource es = esRef.Target as EventSource;
4474 /// Helper used in code:Dispose that removes any references to 'listenerToRemove' in any of the
4475 /// eventSources in the appdomain.
4477 /// The EventListenersLock must be held before calling this routine.
4479 private static void RemoveReferencesToListenerInEventSources(EventListener listenerToRemove)
4481 // Foreach existing EventSource in the appdomain
4482 foreach (WeakReference eventSourceRef in s_EventSources)
4484 EventSource eventSource = eventSourceRef.Target as EventSource;
4485 if (eventSource != null)
4487 // Is the first output dispatcher the dispatcher we are removing?
4488 if (eventSource.m_Dispatchers.m_Listener == listenerToRemove)
4489 eventSource.m_Dispatchers = eventSource.m_Dispatchers.m_Next;
4492 // Remove 'listenerToRemove' from the eventSource.m_Dispatchers linked list.
4493 EventDispatcher prev = eventSource.m_Dispatchers;
4496 EventDispatcher cur = prev.m_Next;
4499 Contract.Assert(false, "EventSource did not have a registered EventListener!");
4502 if (cur.m_Listener == listenerToRemove)
4504 prev.m_Next = cur.m_Next; // Remove entry.
4515 /// Checks internal consistency of EventSources/Listeners.
4517 [Conditional("DEBUG")]
4518 internal static void Validate()
4520 lock (EventListenersLock)
4522 // Get all listeners
4523 Dictionary<EventListener, bool> allListeners = new Dictionary<EventListener, bool>();
4524 EventListener cur = s_Listeners;
4527 allListeners.Add(cur, true);
4531 // For all eventSources
4533 foreach (WeakReference eventSourceRef in s_EventSources)
4536 EventSource eventSource = eventSourceRef.Target as EventSource;
4537 if (eventSource == null)
4539 Contract.Assert(eventSource.m_id == id, "Unexpected event source ID.");
4541 // None listeners on eventSources exist in the dispatcher list.
4542 EventDispatcher dispatcher = eventSource.m_Dispatchers;
4543 while (dispatcher != null)
4545 Contract.Assert(allListeners.ContainsKey(dispatcher.m_Listener), "EventSource has a listener not on the global list.");
4546 dispatcher = dispatcher.m_Next;
4549 // Every dispatcher is on Dispatcher List of every eventSource.
4550 foreach (EventListener listener in allListeners.Keys)
4552 dispatcher = eventSource.m_Dispatchers;
4555 Contract.Assert(dispatcher != null, "Listener is not on all eventSources.");
4556 if (dispatcher.m_Listener == listener)
4558 dispatcher = dispatcher.m_Next;
4566 /// Gets a global lock that is intended to protect the code:s_Listeners linked list and the
4567 /// code:s_EventSources WeakReference list. (We happen to use the s_EventSources list as
4568 /// the lock object)
4570 internal static object EventListenersLock
4574 if (s_EventSources == null)
4575 Interlocked.CompareExchange(ref s_EventSources, new List<WeakReference>(2), null);
4576 return s_EventSources;
4580 private void CallBackForExistingEventSources(bool addToListenersList, EventHandler<EventSourceCreatedEventArgs> callback)
4582 lock (EventListenersLock)
4584 // Disallow creating EventListener reentrancy.
4585 if (s_CreatingListener)
4586 throw new InvalidOperationException(Environment.GetResourceString("EventSource_ListenerCreatedInsideCallback"));
4590 s_CreatingListener = true;
4592 if (addToListenersList)
4594 // Add to list of listeners in the system, do this BEFORE firing the 'OnEventSourceCreated' so that
4595 // Those added sources see this listener.
4596 this.m_Next = s_Listeners;
4600 // Find all existing eventSources call OnEventSourceCreated to 'catchup'
4601 // Note that we DO have reentrancy here because 'AddListener' calls out to user code (via OnEventSourceCreated callback)
4602 // We tolerate this by iterating over a copy of the list here. New event sources will take care of adding listeners themselves
4603 // EventSources are not guaranteed to be added at the end of the s_EventSource list -- We re-use slots when a new source
4605 WeakReference[] eventSourcesSnapshot = s_EventSources.ToArray();
4607 for (int i = 0; i < eventSourcesSnapshot.Length; i++)
4609 WeakReference eventSourceRef = eventSourcesSnapshot[i];
4610 EventSource eventSource = eventSourceRef.Target as EventSource;
4611 if (eventSource != null)
4613 EventSourceCreatedEventArgs args = new EventSourceCreatedEventArgs();
4614 args.EventSource = eventSource;
4615 callback(this, args);
4623 s_CreatingListener = false;
4630 internal volatile EventListener m_Next; // These form a linked list in s_Listeners
4631 #if FEATURE_ACTIVITYSAMPLING
4632 internal ActivityFilter m_activityFilter; // If we are filtering by activity on this Listener, this keeps track of it.
4633 #endif // FEATURE_ACTIVITYSAMPLING
4638 /// The list of all listeners in the appdomain. Listeners must be explicitly disposed to remove themselves
4639 /// from this list. Note that EventSources point to their listener but NOT the reverse.
4641 internal static EventListener s_Listeners;
4643 /// The list of all active eventSources in the appdomain. Note that eventSources do NOT
4644 /// remove themselves from this list this is a weak list and the GC that removes them may
4645 /// not have happened yet. Thus it can contain event sources that are dead (thus you have
4646 /// to filter those out.
4648 internal static List<WeakReference> s_EventSources;
4651 /// Used to disallow reentrancy.
4653 private static bool s_CreatingListener = false;
4656 /// Used to register AD/Process shutdown callbacks.
4658 private static bool s_EventSourceShutdownRegistered = false;
4663 /// Passed to the code:EventSource.OnEventCommand callback
4665 public class EventCommandEventArgs : EventArgs
4668 /// Gets the command for the callback.
4670 public EventCommand Command { get; internal set; }
4673 /// Gets the arguments for the callback.
4675 public IDictionary<String, String> Arguments { get; internal set; }
4678 /// Enables the event that has the specified identifier.
4680 /// <param name="eventId">Event ID of event to be enabled</param>
4681 /// <returns>true if eventId is in range</returns>
4682 public bool EnableEvent(int eventId)
4684 if (Command != EventCommand.Enable && Command != EventCommand.Disable)
4685 throw new InvalidOperationException();
4686 return eventSource.EnableEventForDispatcher(dispatcher, eventId, true);
4690 /// Disables the event that have the specified identifier.
4692 /// <param name="eventId">Event ID of event to be disabled</param>
4693 /// <returns>true if eventId is in range</returns>
4694 public bool DisableEvent(int eventId)
4696 if (Command != EventCommand.Enable && Command != EventCommand.Disable)
4697 throw new InvalidOperationException();
4698 return eventSource.EnableEventForDispatcher(dispatcher, eventId, false);
4703 internal EventCommandEventArgs(EventCommand command, IDictionary<string, string> arguments, EventSource eventSource,
4704 EventListener listener, int perEventSourceSessionId, int etwSessionId, bool enable, EventLevel level, EventKeywords matchAnyKeyword)
4706 this.Command = command;
4707 this.Arguments = arguments;
4708 this.eventSource = eventSource;
4709 this.listener = listener;
4710 this.perEventSourceSessionId = perEventSourceSessionId;
4711 this.etwSessionId = etwSessionId;
4712 this.enable = enable;
4714 this.matchAnyKeyword = matchAnyKeyword;
4717 internal EventSource eventSource;
4718 internal EventDispatcher dispatcher;
4720 // These are the arguments of sendCommand and are only used for deferring commands until after we are fully initialized.
4721 internal EventListener listener;
4722 internal int perEventSourceSessionId;
4723 internal int etwSessionId;
4724 internal bool enable;
4725 internal EventLevel level;
4726 internal EventKeywords matchAnyKeyword;
4727 internal EventCommandEventArgs nextCommand; // We form a linked list of these deferred commands.
4733 /// EventSourceCreatedEventArgs is passed to <see cref="EventListener.EventSourceCreated"/>
4735 public class EventSourceCreatedEventArgs : EventArgs
4738 /// The EventSource that is attaching to the listener.
4740 public EventSource EventSource
4748 /// EventWrittenEventArgs is passed to the user-provided override for
4749 /// <see cref="EventListener.OnEventWritten"/> when an event is fired.
4751 public class EventWrittenEventArgs : EventArgs
4754 /// The name of the event.
4756 public string EventName
4760 if (m_eventName != null || EventId < 0) // TraceLogging convention EventID == -1
4765 return m_eventSource.m_eventData[EventId].Name;
4769 m_eventName = value;
4774 /// Gets the event ID for the event that was written.
4776 public int EventId { get; internal set; }
4779 /// Gets the activity ID for the thread on which the event was written.
4781 public Guid ActivityId
4783 [System.Security.SecurityCritical]
4784 get { return EventSource.CurrentThreadActivityId; }
4788 /// Gets the related activity ID if one was specified when the event was written.
4790 public Guid RelatedActivityId
4792 [System.Security.SecurityCritical]
4798 /// Gets the payload for the event.
4800 public ReadOnlyCollection<Object> Payload { get; internal set; }
4803 /// Gets the payload argument names.
4805 public ReadOnlyCollection<string> PayloadNames
4809 // For contract based events we create the list lazily.
4810 if (m_payloadNames == null)
4812 // Self described events are identified by id -1.
4813 Contract.Assert(EventId != -1);
4815 var names = new List<string>();
4816 foreach (var parameter in m_eventSource.m_eventData[EventId].Parameters)
4818 names.Add(parameter.Name);
4820 m_payloadNames = new ReadOnlyCollection<string>(names);
4823 return m_payloadNames;
4828 m_payloadNames = value;
4833 /// Gets the event source object.
4835 public EventSource EventSource { get { return m_eventSource; } }
4838 /// Gets the keywords for the event.
4840 public EventKeywords Keywords
4844 if (EventId < 0) // TraceLogging convention EventID == -1
4847 return (EventKeywords)m_eventSource.m_eventData[EventId].Descriptor.Keywords;
4852 /// Gets the operation code for the event.
4854 public EventOpcode Opcode
4858 if (EventId < 0) // TraceLogging convention EventID == -1
4860 return (EventOpcode)m_eventSource.m_eventData[EventId].Descriptor.Opcode;
4865 /// Gets the task for the event.
4867 public EventTask Task
4871 if (EventId < 0) // TraceLogging convention EventID == -1
4872 return EventTask.None;
4874 return (EventTask)m_eventSource.m_eventData[EventId].Descriptor.Task;
4879 /// Any provider/user defined options associated with the event.
4881 public EventTags Tags
4885 if (EventId < 0) // TraceLogging convention EventID == -1
4887 return m_eventSource.m_eventData[EventId].Tags;
4892 /// Gets the message for the event.
4894 public string Message
4898 if (EventId < 0) // TraceLogging convention EventID == -1
4901 return m_eventSource.m_eventData[EventId].Message;
4910 #if FEATURE_MANAGED_ETW_CHANNELS
4912 /// Gets the channel for the event.
4914 public EventChannel Channel
4918 if (EventId < 0) // TraceLogging convention EventID == -1
4919 return EventChannel.None;
4920 return (EventChannel)m_eventSource.m_eventData[EventId].Descriptor.Channel;
4926 /// Gets the version of the event.
4932 if (EventId < 0) // TraceLogging convention EventID == -1
4934 return m_eventSource.m_eventData[EventId].Descriptor.Version;
4939 /// Gets the level for the event.
4941 public EventLevel Level
4945 if (EventId < 0) // TraceLogging convention EventID == -1
4947 return (EventLevel)m_eventSource.m_eventData[EventId].Descriptor.Level;
4952 internal EventWrittenEventArgs(EventSource eventSource)
4954 m_eventSource = eventSource;
4956 private string m_message;
4957 private string m_eventName;
4958 private EventSource m_eventSource;
4959 private ReadOnlyCollection<string> m_payloadNames;
4960 internal EventTags m_tags;
4961 internal EventOpcode m_opcode;
4962 internal EventLevel m_level;
4963 internal EventKeywords m_keywords;
4968 /// Allows customizing defaults and specifying localization support for the event source class to which it is applied.
4970 [AttributeUsage(AttributeTargets.Class)]
4971 public sealed class EventSourceAttribute : Attribute
4974 /// Overrides the ETW name of the event source (which defaults to the class name)
4976 public string Name { get; set; }
4979 /// Overrides the default (calculated) Guid of an EventSource type. Explicitly defining a GUID is discouraged,
4980 /// except when upgrading existing ETW providers to using event sources.
4982 public string Guid { get; set; }
4986 /// EventSources support localization of events. The names used for events, opcodes, tasks, keywords and maps
4987 /// can be localized to several languages if desired. This works by creating a ResX style string table
4988 /// (by simply adding a 'Resource File' to your project). This resource file is given a name e.g.
4989 /// 'DefaultNameSpace.ResourceFileName' which can be passed to the ResourceManager constructor to read the
4990 /// resources. This name is the value of the LocalizationResources property.
4992 /// If LocalizationResources property is non-null, then EventSource will look up the localized strings for events by
4993 /// using the following resource naming scheme
4995 /// <para>* event_EVENTNAME</para>
4996 /// <para>* task_TASKNAME</para>
4997 /// <para>* keyword_KEYWORDNAME</para>
4998 /// <para>* map_MAPNAME</para>
5000 /// where the capitalized name is the name of the event, task, keyword, or map value that should be localized.
5001 /// Note that the localized string for an event corresponds to the Message string, and can have {0} values
5002 /// which represent the payload values.
5005 public string LocalizationResources { get; set; }
5009 /// Any instance methods in a class that subclasses <see cref="EventSource"/> and that return void are
5010 /// assumed by default to be methods that generate an ETW event. Enough information can be deduced from the
5011 /// name of the method and its signature to generate basic schema information for the event. The
5012 /// <see cref="EventAttribute"/> class allows you to specify additional event schema information for an event if
5015 [AttributeUsage(AttributeTargets.Method)]
5016 public sealed class EventAttribute : Attribute
5018 /// <summary>Construct an EventAttribute with specified eventId</summary>
5019 /// <param name="eventId">ID of the ETW event (an integer between 1 and 65535)</param>
5020 public EventAttribute(int eventId) { this.EventId = eventId; Level = EventLevel.Informational; this.m_opcodeSet = false; }
5021 /// <summary>Event's ID</summary>
5022 public int EventId { get; private set; }
5023 /// <summary>Event's severity level: indicates the severity or verbosity of the event</summary>
5024 public EventLevel Level { get; set; }
5025 /// <summary>Event's keywords: allows classification of events by "categories"</summary>
5026 public EventKeywords Keywords { get; set; }
5027 /// <summary>Event's operation code: allows defining operations, generally used with Tasks</summary>
5028 public EventOpcode Opcode
5036 this.m_opcode = value;
5037 this.m_opcodeSet = true;
5041 internal bool IsOpcodeSet
5049 /// <summary>Event's task: allows logical grouping of events</summary>
5050 public EventTask Task { get; set; }
5051 #if FEATURE_MANAGED_ETW_CHANNELS
5052 /// <summary>Event's channel: defines an event log as an additional destination for the event</summary>
5053 public EventChannel Channel { get; set; }
5055 /// <summary>Event's version</summary>
5056 public byte Version { get; set; }
5059 /// This can be specified to enable formatting and localization of the event's payload. You can
5060 /// use standard .NET substitution operators (eg {1}) in the string and they will be replaced
5061 /// with the 'ToString()' of the corresponding part of the event payload.
5063 public string Message { get; set; }
5066 /// User defined options associated with the event. These do not have meaning to the EventSource but
5067 /// are passed through to listeners which given them semantics.
5069 public EventTags Tags { get; set; }
5072 /// Allows fine control over the Activity IDs generated by start and stop events
5074 public EventActivityOptions ActivityOptions { get; set; }
5077 EventOpcode m_opcode;
5078 private bool m_opcodeSet;
5083 /// By default all instance methods in a class that subclasses code:EventSource that and return
5084 /// void are assumed to be methods that generate an event. This default can be overridden by specifying
5085 /// the code:NonEventAttribute
5087 [AttributeUsage(AttributeTargets.Method)]
5088 public sealed class NonEventAttribute : Attribute
5091 /// Constructs a default NonEventAttribute
5093 public NonEventAttribute() { }
5096 // FUTURE we may want to expose this at some point once we have a partner that can help us validate the design.
5097 #if FEATURE_MANAGED_ETW_CHANNELS
5099 /// EventChannelAttribute allows customizing channels supported by an EventSource. This attribute must be
5100 /// applied to an member of type EventChannel defined in a Channels class nested in the EventSource class:
5102 /// public static class Channels
5104 /// [Channel(Enabled = true, EventChannelType = EventChannelType.Admin)]
5105 /// public const EventChannel Admin = (EventChannel)16;
5107 /// [Channel(Enabled = false, EventChannelType = EventChannelType.Operational)]
5108 /// public const EventChannel Operational = (EventChannel)17;
5112 [AttributeUsage(AttributeTargets.Field)]
5113 #if FEATURE_ADVANCED_MANAGED_ETW_CHANNELS
5116 class EventChannelAttribute : Attribute
5119 /// Specified whether the channel is enabled by default
5121 public bool Enabled { get; set; }
5124 /// Legal values are in EventChannelType
5126 public EventChannelType EventChannelType { get; set; }
5128 #if FEATURE_ADVANCED_MANAGED_ETW_CHANNELS
5130 /// Specifies the isolation for the channel
5132 public EventChannelIsolation Isolation { get; set; }
5135 /// Specifies an SDDL access descriptor that controls access to the log file that backs the channel.
5136 /// See MSDN ((http://msdn.microsoft.com/en-us/library/windows/desktop/aa382741.aspx) for details.
5138 public string Access { get; set; }
5141 /// Allows importing channels defined in external manifests
5143 public string ImportChannel { get; set; }
5146 // TODO: there is a convention that the name is the Provider/Type Should we provide an override?
5147 // public string Name { get; set; }
5151 /// Allowed channel types
5153 #if FEATURE_ADVANCED_MANAGED_ETW_CHANNELS
5156 enum EventChannelType
5158 /// <summary>The admin channel</summary>
5160 /// <summary>The operational channel</summary>
5162 /// <summary>The Analytic channel</summary>
5164 /// <summary>The debug channel</summary>
5168 #if FEATURE_ADVANCED_MANAGED_ETW_CHANNELS
5170 /// Allowed isolation levels. See MSDN (http://msdn.microsoft.com/en-us/library/windows/desktop/aa382741.aspx)
5171 /// for the default permissions associated with each level. EventChannelIsolation and Access allows control over the
5172 /// access permissions for the channel and backing file.
5175 enum EventChannelIsolation
5178 /// This is the default isolation level. All channels that specify Application isolation use the same ETW session
5182 /// All channels that specify System isolation use the same ETW session
5186 /// Use sparingly! When specifying Custom isolation, a separate ETW session is created for the channel.
5187 /// Using Custom isolation lets you control the access permissions for the channel and backing file.
5188 /// Because there are only 64 ETW sessions available, you should limit your use of Custom isolation.
5196 /// Describes the pre-defined command (EventCommandEventArgs.Command property) that is passed to the OnEventCommand callback.
5198 public enum EventCommand
5201 /// Update EventSource state
5205 /// Request EventSource to generate and send its manifest
5219 #region private classes
5221 #if FEATURE_ACTIVITYSAMPLING
5224 /// ActivityFilter is a helper structure that is used to keep track of run-time state
5225 /// associated with activity filtering. It is 1-1 with EventListeners (logically
5226 /// every listener has one of these, however we actually allocate them lazily), as well
5227 /// as 1-to-1 with tracing-aware EtwSessions.
5229 /// This structure also keeps track of the sampling counts associated with 'trigger'
5230 /// events. Because these trigger events are rare, and you typically only have one of
5231 /// them, we store them here as a linked list.
5233 internal sealed class ActivityFilter : IDisposable
5236 /// Disable all activity filtering for the listener associated with 'filterList',
5237 /// (in the session associated with it) that is triggered by any event in 'source'.
5239 public static void DisableFilter(ref ActivityFilter filterList, EventSource source)
5241 #if !ES_BUILD_STANDALONE
5242 Contract.Assert(Monitor.IsEntered(EventListener.EventListenersLock));
5245 if (filterList == null)
5249 // Remove it from anywhere in the list (except the first element, which has to
5250 // be treated specially)
5251 ActivityFilter prev = filterList;
5255 if (cur.m_providerGuid == source.Guid)
5257 // update TriggersActivityTracking bit
5258 if (cur.m_eventId >= 0 && cur.m_eventId < source.m_eventData.Length)
5259 --source.m_eventData[cur.m_eventId].TriggersActivityTracking;
5261 // Remove it from the linked list.
5262 prev.m_next = cur.m_next;
5263 // dispose of the removed node
5276 // Sadly we have to treat the first element specially in linked list removal in C#
5277 if (filterList.m_providerGuid == source.Guid)
5279 // update TriggersActivityTracking bit
5280 if (filterList.m_eventId >= 0 && filterList.m_eventId < source.m_eventData.Length)
5281 --source.m_eventData[filterList.m_eventId].TriggersActivityTracking;
5283 // We are the first element in the list.
5284 var first = filterList;
5285 filterList = first.m_next;
5286 // dispose of the removed node
5289 // the above might have removed the one ActivityFilter in the session that contains the
5290 // cleanup delegate; re-create the delegate if needed
5291 if (filterList != null)
5293 EnsureActivityCleanupDelegate(filterList);
5298 /// Currently this has "override" semantics. We first disable all filters
5299 /// associated with 'source', and next we add new filters for each entry in the
5300 /// string 'startEvents'. participateInSampling specifies whether non-startEvents
5301 /// always trigger or only trigger when current activity is 'active'.
5303 public static void UpdateFilter(
5304 ref ActivityFilter filterList,
5306 int perEventSourceSessionId,
5309 #if !ES_BUILD_STANDALONE
5310 Contract.Assert(Monitor.IsEntered(EventListener.EventListenersLock));
5313 // first remove all filters associated with 'source'
5314 DisableFilter(ref filterList, source);
5316 if (!string.IsNullOrEmpty(startEvents))
5318 // ActivitySamplingStartEvents is a space-separated list of Event:Frequency pairs.
5319 // The Event may be specified by name or by ID. Errors in parsing such a pair
5320 // result in the error being reported to the listeners, and the pair being ignored.
5321 // E.g. "CustomActivityStart:1000 12:10" specifies that for event CustomActivityStart
5322 // we should initiate activity tracing once every 1000 events, *and* for event ID 12
5323 // we should initiate activity tracing once every 10 events.
5324 string[] activityFilterStrings = startEvents.Split(' ');
5326 for (int i = 0; i < activityFilterStrings.Length; ++i)
5328 string activityFilterString = activityFilterStrings[i];
5331 int colonIdx = activityFilterString.IndexOf(':');
5334 source.ReportOutOfBandMessage("ERROR: Invalid ActivitySamplingStartEvent specification: " +
5335 activityFilterString, false);
5336 // ignore failure...
5339 string sFreq = activityFilterString.Substring(colonIdx + 1);
5340 if (!int.TryParse(sFreq, out sampleFreq))
5342 source.ReportOutOfBandMessage("ERROR: Invalid sampling frequency specification: " + sFreq, false);
5345 activityFilterString = activityFilterString.Substring(0, colonIdx);
5346 if (!int.TryParse(activityFilterString, out eventId))
5350 // see if it's an event name
5351 for (int j = 0; j < source.m_eventData.Length; j++)
5353 EventSource.EventMetadata[] ed = source.m_eventData;
5354 if (ed[j].Name != null && ed[j].Name.Length == activityFilterString.Length &&
5355 string.Compare(ed[j].Name, activityFilterString, StringComparison.OrdinalIgnoreCase) == 0)
5357 eventId = ed[j].Descriptor.EventId;
5362 if (eventId < 0 || eventId >= source.m_eventData.Length)
5364 source.ReportOutOfBandMessage("ERROR: Invalid eventId specification: " + activityFilterString, false);
5367 EnableFilter(ref filterList, source, perEventSourceSessionId, eventId, sampleFreq);
5373 /// Returns the first ActivityFilter from 'filterList' corresponding to 'source'.
5375 public static ActivityFilter GetFilter(ActivityFilter filterList, EventSource source)
5377 for (var af = filterList; af != null; af = af.m_next)
5379 if (af.m_providerGuid == source.Guid && af.m_samplingFreq != -1)
5386 /// Returns a session mask representing all sessions in which the activity
5387 /// associated with the current thread is allowed through the activity filter.
5388 /// If 'triggeringEvent' is true the event MAY be a triggering event. Ideally
5389 /// most of the time this is false as you can guarentee this event is NOT a
5390 /// triggering event. If 'triggeringEvent' is true, then it checks the
5391 /// 'EventSource' and 'eventID' of the event being logged to see if it is actually
5392 /// a trigger. If so it activates the current activity.
5394 /// If 'childActivityID' is present, it will be added to the active set if the
5395 /// current activity is active.
5398 unsafe public static bool PassesActivityFilter(
5399 ActivityFilter filterList,
5400 Guid* childActivityID,
5401 bool triggeringEvent,
5405 Contract.Assert(filterList != null && filterList.m_activeActivities != null);
5406 bool shouldBeLogged = false;
5407 if (triggeringEvent)
5409 for (ActivityFilter af = filterList; af != null; af = af.m_next)
5411 if (eventId == af.m_eventId && source.Guid == af.m_providerGuid)
5413 // Update the sampling count with wrap-around
5414 int curSampleCount, newSampleCount;
5417 curSampleCount = af.m_curSampleCount;
5418 if (curSampleCount <= 1)
5419 newSampleCount = af.m_samplingFreq; // Wrap around, counting down to 1
5421 newSampleCount = curSampleCount - 1;
5423 while (Interlocked.CompareExchange(ref af.m_curSampleCount, newSampleCount, curSampleCount) != curSampleCount);
5424 // If we hit zero, then start tracking the activity.
5425 if (curSampleCount <= 1)
5427 Guid currentActivityId = EventSource.InternalCurrentThreadActivityId;
5428 Tuple<Guid, int> startId;
5429 // only add current activity if it's not already a root activity
5430 if (!af.m_rootActiveActivities.TryGetValue(currentActivityId, out startId))
5432 // EventSource.OutputDebugString(string.Format(" PassesAF - Triggering(session {0}, evt {1})", af.m_perEventSourceSessionId, eventId));
5433 shouldBeLogged = true;
5434 af.m_activeActivities[currentActivityId] = Environment.TickCount;
5435 af.m_rootActiveActivities[currentActivityId] = Tuple.Create(source.Guid, eventId);
5440 // a start event following a triggering start event
5441 Guid currentActivityId = EventSource.InternalCurrentThreadActivityId;
5442 Tuple<Guid, int> startId;
5443 // only remove current activity if we added it
5444 if (af.m_rootActiveActivities.TryGetValue(currentActivityId, out startId) &&
5445 startId.Item1 == source.Guid && startId.Item2 == eventId)
5447 // EventSource.OutputDebugString(string.Format("Activity dying: {0} -> StartEvent({1})", currentActivityId, eventId));
5448 // remove activity only from current logging scope (af)
5450 af.m_activeActivities.TryRemove(currentActivityId, out dummy);
5458 var activeActivities = GetActiveActivities(filterList);
5459 if (activeActivities != null)
5461 // if we hadn't already determined this should be logged, test further
5462 if (!shouldBeLogged)
5464 shouldBeLogged = !activeActivities.IsEmpty &&
5465 activeActivities.ContainsKey(EventSource.InternalCurrentThreadActivityId);
5467 if (shouldBeLogged && childActivityID != null &&
5468 ((EventOpcode)source.m_eventData[eventId].Descriptor.Opcode == EventOpcode.Send))
5470 FlowActivityIfNeeded(filterList, null, childActivityID);
5471 // EventSource.OutputDebugString(string.Format(" PassesAF - activity {0}", *childActivityID));
5474 // EventSource.OutputDebugString(string.Format(" PassesAF - shouldBeLogged(evt {0}) = {1:x}", eventId, shouldBeLogged));
5475 return shouldBeLogged;
5478 [System.Security.SecuritySafeCritical]
5479 public static bool IsCurrentActivityActive(ActivityFilter filterList)
5481 var activeActivities = GetActiveActivities(filterList);
5482 if (activeActivities != null &&
5483 activeActivities.ContainsKey(EventSource.InternalCurrentThreadActivityId))
5490 /// For the EventListener/EtwSession associated with 'filterList', add 'childActivityid'
5491 /// to list of active activities IF 'currentActivityId' is also active. Passing in a null
5492 /// value for 'currentActivityid' is an indication tha caller has already verified
5493 /// that the current activity is active.
5496 unsafe public static void FlowActivityIfNeeded(ActivityFilter filterList, Guid* currentActivityId, Guid* childActivityID)
5498 Contract.Assert(childActivityID != null);
5500 var activeActivities = GetActiveActivities(filterList);
5501 Contract.Assert(activeActivities != null);
5503 // take currentActivityId == null to mean we *know* the current activity is "active"
5504 if (currentActivityId != null && !activeActivities.ContainsKey(*currentActivityId))
5507 if (activeActivities.Count > MaxActivityTrackCount)
5509 TrimActiveActivityStore(activeActivities);
5510 // make sure current activity is still in the set:
5511 activeActivities[EventSource.InternalCurrentThreadActivityId] = Environment.TickCount;
5513 // add child activity to list of actives
5514 activeActivities[*childActivityID] = Environment.TickCount;
5520 public static void UpdateKwdTriggers(ActivityFilter activityFilter, Guid sourceGuid, EventSource source, EventKeywords sessKeywords)
5522 for (var af = activityFilter; af != null; af = af.m_next)
5524 if ((sourceGuid == af.m_providerGuid) &&
5525 (source.m_eventData[af.m_eventId].TriggersActivityTracking > 0 ||
5526 ((EventOpcode)source.m_eventData[af.m_eventId].Descriptor.Opcode == EventOpcode.Send)))
5528 // we could be more precise here, if we tracked 'anykeywords' per session
5531 source.m_keywordTriggers |= (source.m_eventData[af.m_eventId].Descriptor.Keywords & (long)sessKeywords);
5538 /// For the EventSource specified by 'sourceGuid' and the EventListener/EtwSession
5539 /// associated with 'this' ActivityFilter list, return configured sequence of
5540 /// [eventId, sampleFreq] pairs that defines the sampling policy.
5542 public IEnumerable<Tuple<int, int>> GetFilterAsTuple(Guid sourceGuid)
5544 for (ActivityFilter af = this; af != null; af = af.m_next)
5546 if (af.m_providerGuid == sourceGuid)
5547 yield return Tuple.Create(af.m_eventId, af.m_samplingFreq);
5552 /// The cleanup being performed consists of removing the m_myActivityDelegate from
5553 /// the static s_activityDying, therefore allowing the ActivityFilter to be reclaimed.
5555 public void Dispose()
5557 #if !ES_BUILD_STANDALONE
5558 Contract.Assert(Monitor.IsEntered(EventListener.EventListenersLock));
5560 // m_myActivityDelegate is still alive (held by the static EventSource.s_activityDying).
5561 // Therefore we are ok to take a dependency on m_myActivityDelegate being valid even
5562 // during the finalization of the ActivityFilter
5563 if (m_myActivityDelegate != null)
5565 EventSource.s_activityDying = (Action<Guid>)Delegate.Remove(EventSource.s_activityDying, m_myActivityDelegate);
5566 m_myActivityDelegate = null;
5573 /// Creates a new ActivityFilter that is triggered by 'eventId' from 'source' ever
5574 /// 'samplingFreq' times the event fires. You can have several of these forming a
5577 private ActivityFilter(EventSource source, int perEventSourceSessionId, int eventId, int samplingFreq, ActivityFilter existingFilter = null)
5579 m_providerGuid = source.Guid;
5580 m_perEventSourceSessionId = perEventSourceSessionId;
5581 m_eventId = eventId;
5582 m_samplingFreq = samplingFreq;
5583 m_next = existingFilter;
5585 Contract.Assert(existingFilter == null ||
5586 (existingFilter.m_activeActivities == null) == (existingFilter.m_rootActiveActivities == null));
5588 // if this is the first filter we add for this session, we need to create a new
5589 // table of activities. m_activeActivities is common across EventSources in the same
5591 ConcurrentDictionary<Guid, int> activeActivities = null;
5592 if (existingFilter == null ||
5593 (activeActivities = GetActiveActivities(existingFilter)) == null)
5595 m_activeActivities = new ConcurrentDictionary<Guid, int>();
5596 m_rootActiveActivities = new ConcurrentDictionary<Guid, Tuple<Guid, int>>();
5598 // Add a delegate to the 'SetCurrentThreadToActivity callback so that I remove 'dead' activities
5599 m_myActivityDelegate = GetActivityDyingDelegate(this);
5600 EventSource.s_activityDying = (Action<Guid>)Delegate.Combine(EventSource.s_activityDying, m_myActivityDelegate);
5604 m_activeActivities = activeActivities;
5605 m_rootActiveActivities = existingFilter.m_rootActiveActivities;
5611 /// Ensure there's at least one ActivityFilter in the 'filterList' that contains an
5612 /// activity-removing delegate for the listener/session associated with 'filterList'.
5614 private static void EnsureActivityCleanupDelegate(ActivityFilter filterList)
5616 if (filterList == null)
5619 for (ActivityFilter af = filterList; af != null; af = af.m_next)
5621 if (af.m_myActivityDelegate != null)
5625 // we didn't find a delegate
5626 filterList.m_myActivityDelegate = GetActivityDyingDelegate(filterList);
5627 EventSource.s_activityDying = (Action<Guid>)Delegate.Combine(EventSource.s_activityDying, filterList.m_myActivityDelegate);
5631 /// Builds the delegate to be called when an activity is dying. This is responsible
5632 /// for performing whatever cleanup is needed for the ActivityFilter list passed in.
5633 /// This gets "added" to EventSource.s_activityDying and ends up being called from
5634 /// EventSource.SetCurrentThreadActivityId and ActivityFilter.PassesActivityFilter.
5636 /// <returns>The delegate to be called when an activity is dying</returns>
5637 private static Action<Guid> GetActivityDyingDelegate(ActivityFilter filterList)
5639 return (Guid oldActivity) =>
5642 filterList.m_activeActivities.TryRemove(oldActivity, out dummy);
5643 Tuple<Guid, int> dummyTuple;
5644 filterList.m_rootActiveActivities.TryRemove(oldActivity, out dummyTuple);
5649 /// Enables activity filtering for the listener associated with 'filterList', triggering on
5650 /// the event 'eventID' from 'source' with a sampling frequency of 'samplingFreq'
5652 /// if 'eventID' is out of range (e.g. negative), it means we are not triggering (but we are
5653 /// activitySampling if something else triggered).
5655 /// <returns>true if activity sampling is enabled the samplingFreq is non-zero </returns>
5656 private static bool EnableFilter(ref ActivityFilter filterList, EventSource source, int perEventSourceSessionId, int eventId, int samplingFreq)
5658 #if !ES_BUILD_STANDALONE
5659 Contract.Assert(Monitor.IsEntered(EventListener.EventListenersLock));
5661 Contract.Assert(samplingFreq > 0);
5662 Contract.Assert(eventId >= 0);
5664 filterList = new ActivityFilter(source, perEventSourceSessionId, eventId, samplingFreq, filterList);
5666 // Mark the 'quick Check' that indicates this is a trigger event.
5667 // If eventId is out of range then this mark is not done which has the effect of ignoring
5669 if (0 <= eventId && eventId < source.m_eventData.Length)
5670 ++source.m_eventData[eventId].TriggersActivityTracking;
5676 /// Normally this code never runs, it is here just to prevent run-away resource usage.
5678 private static void TrimActiveActivityStore(ConcurrentDictionary<Guid, int> activities)
5680 if (activities.Count > MaxActivityTrackCount)
5682 // Remove half of the oldest activity ids.
5683 var keyValues = activities.ToArray();
5684 var tickNow = Environment.TickCount;
5686 // Sort by age, taking into account wrap-around. As long as x and y are within
5687 // 23 days of now then (0x7FFFFFFF & (tickNow - x.Value)) is the delta (even if
5688 // TickCount wraps). I then sort by DESCENDING age. (that is oldest value first)
5689 Array.Sort(keyValues, (x, y) => (0x7FFFFFFF & (tickNow - y.Value)) - (0x7FFFFFFF & (tickNow - x.Value)));
5690 for (int i = 0; i < keyValues.Length / 2; i++)
5693 activities.TryRemove(keyValues[i].Key, out dummy);
5698 private static ConcurrentDictionary<Guid, int> GetActiveActivities(
5699 ActivityFilter filterList)
5701 for (ActivityFilter af = filterList; af != null; af = af.m_next)
5703 if (af.m_activeActivities != null)
5704 return af.m_activeActivities;
5709 // m_activeActivities always points to the sample dictionary for EVERY ActivityFilter
5710 // in the m_next list. The 'int' value in the m_activities set is a timestamp
5711 // (Environment.TickCount) of when the entry was put in the system and is used to
5712 // remove 'old' entries that if the set gets too big.
5713 ConcurrentDictionary<Guid, int> m_activeActivities;
5715 // m_rootActiveActivities holds the "root" active activities, i.e. the activities
5716 // that were marked as active because a Start event fired on them. We need to keep
5717 // track of these to enable sampling in the scenario of an app's main thread that
5718 // never explicitly sets distinct activity IDs as it executes. To handle these
5719 // situations we manufacture a Guid from the thread's ID, and:
5720 // (a) we consider the firing of a start event when the sampling counter reaches
5721 // zero to mark the beginning of an interesting activity, and
5722 // (b) we consider the very next firing of the same start event to mark the
5723 // ending of that activity.
5724 // We use a ConcurrentDictionary to avoid taking explicit locks.
5725 // The key (a guid) represents the activity ID of the root active activity
5726 // The value is made up of the Guid of the event provider and the eventId of
5728 ConcurrentDictionary<Guid, Tuple<Guid, int>> m_rootActiveActivities;
5729 Guid m_providerGuid; // We use the GUID rather than object identity because we don't want to keep the eventSource alive
5730 int m_eventId; // triggering event
5731 int m_samplingFreq; // Counter reset to this when it hits 0
5732 int m_curSampleCount; // We count down to 0 and then activate the activity.
5733 int m_perEventSourceSessionId; // session ID bit for ETW, 0 for EventListeners
5735 const int MaxActivityTrackCount = 100000; // maximum number of tracked activities
5737 ActivityFilter m_next; // We create a linked list of these
5738 Action<Guid> m_myActivityDelegate;
5744 /// An EtwSession instance represents an activity-tracing-aware ETW session. Since these
5745 /// are limited to 8 concurrent sessions per machine (currently) we're going to store
5746 /// the active ones in a singly linked list.
5748 internal class EtwSession
5750 public static EtwSession GetEtwSession(int etwSessionId, bool bCreateIfNeeded = false)
5752 if (etwSessionId < 0)
5755 EtwSession etwSession;
5756 foreach (var wrEtwSession in s_etwSessions)
5758 #if ES_BUILD_STANDALONE
5759 if ((etwSession = (EtwSession) wrEtwSession.Target) != null && etwSession.m_etwSessionId == etwSessionId)
5762 if (wrEtwSession.TryGetTarget(out etwSession) && etwSession.m_etwSessionId == etwSessionId)
5767 if (!bCreateIfNeeded)
5770 #if ES_BUILD_STANDALONE
5771 if (s_etwSessions == null)
5772 s_etwSessions = new List<WeakReference>();
5774 etwSession = new EtwSession(etwSessionId);
5775 s_etwSessions.Add(new WeakReference(etwSession));
5777 if (s_etwSessions == null)
5778 s_etwSessions = new List<WeakReference<EtwSession>>();
5780 etwSession = new EtwSession(etwSessionId);
5781 s_etwSessions.Add(new WeakReference<EtwSession>(etwSession));
5784 if (s_etwSessions.Count > s_thrSessionCount)
5791 public static void RemoveEtwSession(EtwSession etwSession)
5793 Contract.Assert(etwSession != null);
5794 if (s_etwSessions == null || etwSession == null)
5797 s_etwSessions.RemoveAll((wrEtwSession) =>
5800 #if ES_BUILD_STANDALONE
5801 return (session = (EtwSession) wrEtwSession.Target) != null &&
5802 (session.m_etwSessionId == etwSession.m_etwSessionId);
5804 return wrEtwSession.TryGetTarget(out session) &&
5805 (session.m_etwSessionId == etwSession.m_etwSessionId);
5809 if (s_etwSessions.Count > s_thrSessionCount)
5813 private static void TrimGlobalList()
5815 if (s_etwSessions == null)
5818 s_etwSessions.RemoveAll((wrEtwSession) =>
5820 #if ES_BUILD_STANDALONE
5821 return wrEtwSession.Target == null;
5824 return !wrEtwSession.TryGetTarget(out session);
5829 private EtwSession(int etwSessionId)
5831 m_etwSessionId = etwSessionId;
5834 public readonly int m_etwSessionId; // ETW session ID (as retrieved by EventProvider)
5835 public ActivityFilter m_activityFilter; // all filters enabled for this session
5837 #if ES_BUILD_STANDALONE
5838 private static List<WeakReference> s_etwSessions = new List<WeakReference>();
5840 private static List<WeakReference<EtwSession>> s_etwSessions = new List<WeakReference<EtwSession>>();
5842 private const int s_thrSessionCount = 16;
5845 #endif // FEATURE_ACTIVITYSAMPLING
5847 // holds a bitfield representing a session mask
5849 /// A SessionMask represents a set of (at most MAX) sessions as a bit mask. The perEventSourceSessionId
5850 /// is the index in the SessionMask of the bit that will be set. These can translate to
5851 /// EventSource's reserved keywords bits using the provided ToEventKeywords() and
5852 /// FromEventKeywords() methods.
5854 internal struct SessionMask
5856 public SessionMask(SessionMask m)
5857 { m_mask = m.m_mask; }
5859 public SessionMask(uint mask = 0)
5860 { m_mask = mask & MASK; }
5862 public bool IsEqualOrSupersetOf(SessionMask m)
5864 return (this.m_mask | m.m_mask) == this.m_mask;
5867 public static SessionMask All
5869 get { return new SessionMask(MASK); }
5872 public static SessionMask FromId(int perEventSourceSessionId)
5874 Contract.Assert(perEventSourceSessionId < MAX);
5875 return new SessionMask((uint)1 << perEventSourceSessionId);
5878 public ulong ToEventKeywords()
5880 return (ulong)m_mask << SHIFT_SESSION_TO_KEYWORD;
5883 public static SessionMask FromEventKeywords(ulong m)
5885 return new SessionMask((uint)(m >> SHIFT_SESSION_TO_KEYWORD));
5888 public bool this[int perEventSourceSessionId]
5892 Contract.Assert(perEventSourceSessionId < MAX);
5893 return (m_mask & (1 << perEventSourceSessionId)) != 0;
5897 Contract.Assert(perEventSourceSessionId < MAX);
5898 if (value) m_mask |= ((uint)1 << perEventSourceSessionId);
5899 else m_mask &= ~((uint)1 << perEventSourceSessionId);
5903 public static SessionMask operator |(SessionMask m1, SessionMask m2)
5905 return new SessionMask(m1.m_mask | m2.m_mask);
5908 public static SessionMask operator &(SessionMask m1, SessionMask m2)
5910 return new SessionMask(m1.m_mask & m2.m_mask);
5913 public static SessionMask operator ^(SessionMask m1, SessionMask m2)
5915 return new SessionMask(m1.m_mask ^ m2.m_mask);
5918 public static SessionMask operator ~(SessionMask m)
5920 return new SessionMask(MASK & ~(m.m_mask));
5923 public static explicit operator ulong(SessionMask m)
5924 { return m.m_mask; }
5926 public static explicit operator uint(SessionMask m)
5927 { return m.m_mask; }
5929 private uint m_mask;
5931 internal const int SHIFT_SESSION_TO_KEYWORD = 44; // bits 44-47 inclusive are reserved
5932 internal const uint MASK = 0x0fU; // the mask of 4 reserved bits
5933 internal const uint MAX = 4; // maximum number of simultaneous ETW sessions supported
5937 /// code:EventDispatchers are a simple 'helper' structure that holds the filtering state
5938 /// (m_EventEnabled) for a particular EventSource X EventListener tuple
5940 /// Thus a single EventListener may have many EventDispatchers (one for every EventSource
5941 /// that that EventListener has activate) and a Single EventSource may also have many
5942 /// event Dispatchers (one for every EventListener that has activated it).
5944 /// Logically a particular EventDispatcher belongs to exactly one EventSource and exactly
5945 /// one EventListener (alhtough EventDispatcher does not 'remember' the EventSource it is
5946 /// associated with.
5948 internal class EventDispatcher
5950 internal EventDispatcher(EventDispatcher next, bool[] eventEnabled, EventListener listener)
5953 m_EventEnabled = eventEnabled;
5954 m_Listener = listener;
5958 readonly internal EventListener m_Listener; // The dispatcher this entry is for
5959 internal bool[] m_EventEnabled; // For every event in a the eventSource, is it enabled?
5960 #if FEATURE_ACTIVITYSAMPLING
5961 internal bool m_activityFilteringEnabled; // does THIS EventSource have activity filtering turned on for this listener?
5962 #endif // FEATURE_ACTIVITYSAMPLING
5964 // Only guarenteed to exist after a InsureInit()
5965 internal EventDispatcher m_Next; // These form a linked list in code:EventSource.m_Dispatchers
5966 // Of all listeners for that eventSource.
5970 /// Flags that can be used with EventSource.GenerateManifest to control how the ETW manifest for the EventSource is
5974 public enum EventManifestOptions
5977 /// Only the resources associated with current UI culture are included in the manifest
5981 /// Throw exceptions for any inconsistency encountered
5985 /// Generate a "resources" node under "localization" for every satellite assembly provided
5989 /// Generate the manifest only if the event source needs to be registered on the machine,
5990 /// otherwise return null (but still perform validation if Strict is specified)
5992 OnlyIfNeededForRegistration = 0x4,
5994 /// When generating the manifest do *not* enforce the rule that the current EventSource class
5995 /// must be the base class for the user-defined type passed in. This allows validation of .net
5996 /// event sources using the new validation code
5998 AllowEventSourceOverride = 0x8,
6002 /// ManifestBuilder is designed to isolate the details of the message of the event from the
6003 /// rest of EventSource. This one happens to create XML.
6005 internal class ManifestBuilder
6008 /// Build a manifest for 'providerName' with the given GUID, which will be packaged into 'dllName'.
6009 /// 'resources, is a resource manager. If specified all messages are localized using that manager.
6011 public ManifestBuilder(string providerName, Guid providerGuid, string dllName, ResourceManager resources,
6012 EventManifestOptions flags)
6014 #if FEATURE_MANAGED_ETW_CHANNELS
6015 this.providerName = providerName;
6019 this.resources = resources;
6020 sb = new StringBuilder();
6021 events = new StringBuilder();
6022 templates = new StringBuilder();
6023 opcodeTab = new Dictionary<int, string>();
6024 stringTab = new Dictionary<string, string>();
6025 errors = new List<string>();
6026 perEventByteArrayArgIndices = new Dictionary<string, List<int>>();
6028 sb.AppendLine("<instrumentationManifest xmlns=\"http://schemas.microsoft.com/win/2004/08/events\">");
6029 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\">");
6030 sb.AppendLine(" <events xmlns=\"http://schemas.microsoft.com/win/2004/08/events\">");
6031 sb.Append("<provider name=\"").Append(providerName).
6032 Append("\" guid=\"{").Append(providerGuid.ToString()).Append("}");
6033 if (dllName != null)
6034 sb.Append("\" resourceFileName=\"").Append(dllName).Append("\" messageFileName=\"").Append(dllName);
6036 var symbolsName = providerName.Replace("-", "").Replace(".", "_"); // Period and - are illegal replace them.
6037 sb.Append("\" symbol=\"").Append(symbolsName);
6038 sb.Append("\">").AppendLine();
6041 public void AddOpcode(string name, int value)
6043 if ((flags & EventManifestOptions.Strict) != 0)
6045 if (value <= 10 || value >= 239)
6046 ManifestError(Environment.GetResourceString("EventSource_IllegalOpcodeValue", name, value));
6048 if (opcodeTab.TryGetValue(value, out prevName) && !name.Equals(prevName, StringComparison.Ordinal))
6049 ManifestError(Environment.GetResourceString("EventSource_OpcodeCollision", name, prevName, value));
6051 opcodeTab[value] = name;
6053 public void AddTask(string name, int value)
6055 if ((flags & EventManifestOptions.Strict) != 0)
6057 if (value <= 0 || value >= 65535)
6058 ManifestError(Environment.GetResourceString("EventSource_IllegalTaskValue", name, value));
6060 if (taskTab != null && taskTab.TryGetValue(value, out prevName) && !name.Equals(prevName, StringComparison.Ordinal))
6061 ManifestError(Environment.GetResourceString("EventSource_TaskCollision", name, prevName, value));
6063 if (taskTab == null)
6064 taskTab = new Dictionary<int, string>();
6065 taskTab[value] = name;
6067 public void AddKeyword(string name, ulong value)
6069 if ((value & (value - 1)) != 0) // Is it a power of 2?
6070 ManifestError(Environment.GetResourceString("EventSource_KeywordNeedPowerOfTwo", "0x" + value.ToString("x", CultureInfo.CurrentCulture), name), true);
6071 if ((flags & EventManifestOptions.Strict) != 0)
6073 if (value >= 0x0000100000000000UL && !name.StartsWith("Session", StringComparison.Ordinal))
6074 ManifestError(Environment.GetResourceString("EventSource_IllegalKeywordsValue", name, "0x" + value.ToString("x", CultureInfo.CurrentCulture)));
6076 if (keywordTab != null && keywordTab.TryGetValue(value, out prevName) && !name.Equals(prevName, StringComparison.Ordinal))
6077 ManifestError(Environment.GetResourceString("EventSource_KeywordCollision", name, prevName, "0x" + value.ToString("x", CultureInfo.CurrentCulture)));
6079 if (keywordTab == null)
6080 keywordTab = new Dictionary<ulong, string>();
6081 keywordTab[value] = name;
6084 #if FEATURE_MANAGED_ETW_CHANNELS
6086 /// Add a channel. channelAttribute can be null
6088 public void AddChannel(string name, int value, EventChannelAttribute channelAttribute)
6090 EventChannel chValue = (EventChannel)value;
6091 if (value < (int)EventChannel.Admin || value > 255)
6092 ManifestError(Environment.GetResourceString("EventSource_EventChannelOutOfRange", name, value));
6093 else if (chValue >= EventChannel.Admin && chValue <= EventChannel.Debug &&
6094 channelAttribute != null && EventChannelToChannelType(chValue) != channelAttribute.EventChannelType)
6096 // we want to ensure developers do not define EventChannels that conflict with the builtin ones,
6097 // but we want to allow them to override the default ones...
6098 ManifestError(Environment.GetResourceString("EventSource_ChannelTypeDoesNotMatchEventChannelValue",
6099 name, ((EventChannel)value).ToString()));
6102 // TODO: validate there are no conflicting manifest exposed names (generally following the format "provider/type")
6104 ulong kwd = GetChannelKeyword(chValue);
6106 if (channelTab == null)
6107 channelTab = new Dictionary<int, ChannelInfo>(4);
6108 channelTab[value] = new ChannelInfo { Name = name, Keywords = kwd, Attribs = channelAttribute };
6111 private EventChannelType EventChannelToChannelType(EventChannel channel)
6113 #if !ES_BUILD_STANDALONE
6114 Contract.Assert(channel >= EventChannel.Admin && channel <= EventChannel.Debug);
6116 return (EventChannelType)((int)channel - (int)EventChannel.Admin + (int)EventChannelType.Admin);
6118 private EventChannelAttribute GetDefaultChannelAttribute(EventChannel channel)
6120 EventChannelAttribute attrib = new EventChannelAttribute();
6121 attrib.EventChannelType = EventChannelToChannelType(channel);
6122 if (attrib.EventChannelType <= EventChannelType.Operational)
6123 attrib.Enabled = true;
6127 public ulong[] GetChannelData()
6129 if (this.channelTab == null)
6131 return new ulong[0];
6134 // We create an array indexed by the channel id for fast look up.
6135 // E.g. channelMask[Admin] will give you the bit mask for Admin channel.
6137 foreach (var item in this.channelTab.Keys)
6145 ulong[] channelMask = new ulong[maxkey + 1];
6146 foreach (var item in this.channelTab)
6148 channelMask[item.Key] = item.Value.Keywords;
6155 public void StartEvent(string eventName, EventAttribute eventAttribute)
6157 Contract.Assert(numParams == 0);
6158 Contract.Assert(this.eventName == null);
6159 this.eventName = eventName;
6161 byteArrArgIndices = null;
6163 events.Append(" <event").
6164 Append(" value=\"").Append(eventAttribute.EventId).Append("\"").
6165 Append(" version=\"").Append(eventAttribute.Version).Append("\"").
6166 Append(" level=\"").Append(GetLevelName(eventAttribute.Level)).Append("\"").
6167 Append(" symbol=\"").Append(eventName).Append("\"");
6169 // at this point we add to the manifest's stringTab a message that is as-of-yet
6170 // "untranslated to manifest convention", b/c we don't have the number or position
6171 // of any byte[] args (which require string format index updates)
6172 WriteMessageAttrib(events, "event", eventName, eventAttribute.Message);
6174 if (eventAttribute.Keywords != 0)
6175 events.Append(" keywords=\"").Append(GetKeywords((ulong)eventAttribute.Keywords, eventName)).Append("\"");
6176 if (eventAttribute.Opcode != 0)
6177 events.Append(" opcode=\"").Append(GetOpcodeName(eventAttribute.Opcode, eventName)).Append("\"");
6178 if (eventAttribute.Task != 0)
6179 events.Append(" task=\"").Append(GetTaskName(eventAttribute.Task, eventName)).Append("\"");
6180 #if FEATURE_MANAGED_ETW_CHANNELS
6181 if (eventAttribute.Channel != 0)
6183 events.Append(" channel=\"").Append(GetChannelName(eventAttribute.Channel, eventName, eventAttribute.Message)).Append("\"");
6188 public void AddEventParameter(Type type, string name)
6191 templates.Append(" <template tid=\"").Append(eventName).Append("Args\">").AppendLine();
6192 if (type == typeof(byte[]))
6194 // mark this index as "extraneous" (it has no parallel in the managed signature)
6195 // we use these values in TranslateToManifestConvention()
6196 if (byteArrArgIndices == null)
6197 byteArrArgIndices = new List<int>(4);
6198 byteArrArgIndices.Add(numParams);
6200 // add an extra field to the template representing the length of the binary blob
6202 templates.Append(" <data name=\"").Append(name).Append("Size\" inType=\"win:UInt32\"/>").AppendLine();
6205 templates.Append(" <data name=\"").Append(name).Append("\" inType=\"").Append(GetTypeName(type)).Append("\"");
6206 // TODO: for 'byte*' types it assumes the user provided length is named using the same naming convention
6207 // as for 'byte[]' args (blob_arg_name + "Size")
6208 if ((type.IsArray || type.IsPointer) && type.GetElementType() == typeof(byte))
6210 // add "length" attribute to the "blob" field in the template (referencing the field added above)
6211 templates.Append(" length=\"").Append(name).Append("Size\"");
6213 // ETW does not support 64-bit value maps, so we don't specify these as ETW maps
6214 if (type.IsEnum() && Enum.GetUnderlyingType(type) != typeof(UInt64) && Enum.GetUnderlyingType(type) != typeof(Int64))
6216 templates.Append(" map=\"").Append(type.Name).Append("\"");
6217 if (mapsTab == null)
6218 mapsTab = new Dictionary<string, Type>();
6219 if (!mapsTab.ContainsKey(type.Name))
6220 mapsTab.Add(type.Name, type); // Remember that we need to dump the type enumeration
6223 templates.Append("/>").AppendLine();
6225 public void EndEvent()
6229 templates.Append(" </template>").AppendLine();
6230 events.Append(" template=\"").Append(eventName).Append("Args\"");
6232 events.Append("/>").AppendLine();
6234 if (byteArrArgIndices != null)
6235 perEventByteArrayArgIndices[eventName] = byteArrArgIndices;
6237 // at this point we have all the information we need to translate the C# Message
6238 // to the manifest string we'll put in the stringTab
6240 if (stringTab.TryGetValue("event_" + eventName, out msg))
6242 msg = TranslateToManifestConvention(msg, eventName);
6243 stringTab["event_" + eventName] = msg;
6248 byteArrArgIndices = null;
6251 #if FEATURE_MANAGED_ETW_CHANNELS
6252 // Channel keywords are generated one per channel to allow channel based filtering in event viewer. These keywords are autogenerated
6253 // by mc.exe for compiling a manifest and are based on the order of the channels (fields) in the Channels inner class (when advanced
6254 // channel support is enabled), or based on the order the predefined channels appear in the EventAttribute properties (for simple
6255 // support). The manifest generated *MUST* have the channels specified in the same order (that's how our computed keywords are mapped
6256 // to channels by the OS infrastructure).
6257 public ulong GetChannelKeyword(EventChannel channel)
6259 if (channelTab == null)
6261 channelTab = new Dictionary<int, ChannelInfo>(4);
6264 if (channelTab.Count == MaxCountChannels)
6265 ManifestError(Environment.GetResourceString("EventSource_MaxChannelExceeded"));
6267 ulong channelKeyword;
6269 if (!channelTab.TryGetValue((int)channel, out info))
6271 channelKeyword = nextChannelKeywordBit;
6272 nextChannelKeywordBit >>= 1;
6276 channelKeyword = info.Keywords;
6279 return channelKeyword;
6283 public byte[] CreateManifest()
6285 string str = CreateManifestString();
6286 return Encoding.UTF8.GetBytes(str);
6289 public IList<string> Errors { get { return errors; } }
6292 /// When validating an event source it adds the error to the error collection.
6293 /// When not validating it throws an exception if runtimeCritical is "true".
6294 /// Otherwise the error is ignored.
6296 /// <param name="msg"></param>
6297 /// <param name="runtimeCritical"></param>
6298 public void ManifestError(string msg, bool runtimeCritical = false)
6300 if ((flags & EventManifestOptions.Strict) != 0)
6302 else if (runtimeCritical)
6303 throw new ArgumentException(msg);
6306 private string CreateManifestString()
6309 #if FEATURE_MANAGED_ETW_CHANNELS
6310 // Write out the channels
6311 if (channelTab != null)
6313 sb.Append(" <channels>").AppendLine();
6314 var sortedChannels = new List<KeyValuePair<int, ChannelInfo>>();
6315 foreach (KeyValuePair<int, ChannelInfo> p in channelTab) { sortedChannels.Add(p); }
6316 sortedChannels.Sort((p1, p2) => -Comparer<ulong>.Default.Compare(p1.Value.Keywords, p2.Value.Keywords));
6317 foreach (var kvpair in sortedChannels)
6319 int channel = kvpair.Key;
6320 ChannelInfo channelInfo = kvpair.Value;
6322 string channelType = null;
6323 string elementName = "channel";
6324 bool enabled = false;
6325 string fullName = null;
6326 #if FEATURE_ADVANCED_MANAGED_ETW_CHANNELS
6327 string isolation = null;
6328 string access = null;
6330 if (channelInfo.Attribs != null)
6332 var attribs = channelInfo.Attribs;
6333 if (Enum.IsDefined(typeof(EventChannelType), attribs.EventChannelType))
6334 channelType = attribs.EventChannelType.ToString();
6335 enabled = attribs.Enabled;
6336 #if FEATURE_ADVANCED_MANAGED_ETW_CHANNELS
6337 if (attribs.ImportChannel != null)
6339 fullName = attribs.ImportChannel;
6340 elementName = "importChannel";
6342 if (Enum.IsDefined(typeof(EventChannelIsolation), attribs.Isolation))
6343 isolation = attribs.Isolation.ToString();
6344 access = attribs.Access;
6347 if (fullName == null)
6348 fullName = providerName + "/" + channelInfo.Name;
6350 sb.Append(" <").Append(elementName);
6351 sb.Append(" chid=\"").Append(channelInfo.Name).Append("\"");
6352 sb.Append(" name=\"").Append(fullName).Append("\"");
6353 if (elementName == "channel") // not applicable to importChannels.
6355 WriteMessageAttrib(sb, "channel", channelInfo.Name, null);
6356 sb.Append(" value=\"").Append(channel).Append("\"");
6357 if (channelType != null)
6358 sb.Append(" type=\"").Append(channelType).Append("\"");
6359 sb.Append(" enabled=\"").Append(enabled.ToString().ToLower()).Append("\"");
6360 #if FEATURE_ADVANCED_MANAGED_ETW_CHANNELS
6362 sb.Append(" access=\"").Append(access).Append("\"");
6363 if (isolation != null)
6364 sb.Append(" isolation=\"").Append(isolation).Append("\"");
6367 sb.Append("/>").AppendLine();
6369 sb.Append(" </channels>").AppendLine();
6373 // Write out the tasks
6374 if (taskTab != null)
6377 sb.Append(" <tasks>").AppendLine();
6378 var sortedTasks = new List<int>(taskTab.Keys);
6380 foreach (int task in sortedTasks)
6382 sb.Append(" <task");
6383 WriteNameAndMessageAttribs(sb, "task", taskTab[task]);
6384 sb.Append(" value=\"").Append(task).Append("\"/>").AppendLine();
6386 sb.Append(" </tasks>").AppendLine();
6389 // Write out the maps
6390 if (mapsTab != null)
6392 sb.Append(" <maps>").AppendLine();
6393 foreach (Type enumType in mapsTab.Values)
6395 bool isbitmap = EventSource.GetCustomAttributeHelper(enumType, typeof(FlagsAttribute), flags) != null;
6396 string mapKind = isbitmap ? "bitMap" : "valueMap";
6397 sb.Append(" <").Append(mapKind).Append(" name=\"").Append(enumType.Name).Append("\">").AppendLine();
6399 // write out each enum value
6400 FieldInfo[] staticFields = enumType.GetFields(BindingFlags.DeclaredOnly | BindingFlags.Public | BindingFlags.Static);
6401 foreach (FieldInfo staticField in staticFields)
6403 object constantValObj = staticField.GetRawConstantValue();
6404 if (constantValObj != null)
6407 if (constantValObj is int)
6408 hexValue = ((int)constantValObj);
6409 else if (constantValObj is long)
6410 hexValue = ((long)constantValObj);
6414 // ETW requires all bitmap values to be powers of 2. Skip the ones that are not.
6415 // TODO: Warn people about the dropping of values.
6416 if (isbitmap && ((hexValue & (hexValue - 1)) != 0 || hexValue == 0))
6419 sb.Append(" <map value=\"0x").Append(hexValue.ToString("x", CultureInfo.InvariantCulture)).Append("\"");
6420 WriteMessageAttrib(sb, "map", enumType.Name + "." + staticField.Name, staticField.Name);
6421 sb.Append("/>").AppendLine();
6424 sb.Append(" </").Append(mapKind).Append(">").AppendLine();
6426 sb.Append(" </maps>").AppendLine();
6429 // Write out the opcodes
6430 sb.Append(" <opcodes>").AppendLine();
6431 var sortedOpcodes = new List<int>(opcodeTab.Keys);
6432 sortedOpcodes.Sort();
6433 foreach (int opcode in sortedOpcodes)
6435 sb.Append(" <opcode");
6436 WriteNameAndMessageAttribs(sb, "opcode", opcodeTab[opcode]);
6437 sb.Append(" value=\"").Append(opcode).Append("\"/>").AppendLine();
6439 sb.Append(" </opcodes>").AppendLine();
6441 // Write out the keywords
6442 if (keywordTab != null)
6444 sb.Append(" <keywords>").AppendLine();
6445 var sortedKeywords = new List<ulong>(keywordTab.Keys);
6446 sortedKeywords.Sort();
6447 foreach (ulong keyword in sortedKeywords)
6449 sb.Append(" <keyword");
6450 WriteNameAndMessageAttribs(sb, "keyword", keywordTab[keyword]);
6451 sb.Append(" mask=\"0x").Append(keyword.ToString("x", CultureInfo.InvariantCulture)).Append("\"/>").AppendLine();
6453 sb.Append(" </keywords>").AppendLine();
6456 sb.Append(" <events>").AppendLine();
6458 sb.Append(" </events>").AppendLine();
6460 sb.Append(" <templates>").AppendLine();
6461 if (templates.Length > 0)
6463 sb.Append(templates);
6467 // Work around a cornercase ETW issue where a manifest with no templates causes
6468 // ETW events to not get sent to their associated channel.
6469 sb.Append(" <template tid=\"_empty\"></template>").AppendLine();
6471 sb.Append(" </templates>").AppendLine();
6473 sb.Append("</provider>").AppendLine();
6474 sb.Append("</events>").AppendLine();
6475 sb.Append("</instrumentation>").AppendLine();
6477 // Output the localization information.
6478 sb.Append("<localization>").AppendLine();
6480 List<CultureInfo> cultures = null;
6481 if (resources != null && (flags & EventManifestOptions.AllCultures) != 0)
6483 cultures = GetSupportedCultures(resources);
6487 cultures = new List<CultureInfo>();
6488 cultures.Add(CultureInfo.CurrentUICulture);
6490 #if ES_BUILD_STANDALONE
6491 var sortedStrings = new List<string>(stringTab.Keys);
6492 sortedStrings.Sort();
6495 var sortedStrings = new string[stringTab.Keys.Count];
6496 stringTab.Keys.CopyTo(sortedStrings, 0);
6497 // Avoid using public Array.Sort as that attempts to access BinaryCompatibility. Unfortunately FrameworkEventSource gets called
6498 // very early in the app domain creation, when _FusionStore is not set up yet, resulting in a failure to run the static constructory
6499 // for BinaryCompatibility. This failure is then cached and a TypeInitializationException is thrown every time some code attampts to
6500 // access BinaryCompatibility.
6501 ArraySortHelper<string>.IntrospectiveSort(sortedStrings, 0, sortedStrings.Length, Comparer<string>.Default);
6503 foreach (var ci in cultures)
6505 sb.Append(" <resources culture=\"").Append(ci.Name).Append("\">").AppendLine();
6506 sb.Append(" <stringTable>").AppendLine();
6508 foreach (var stringKey in sortedStrings)
6510 string val = GetLocalizedMessage(stringKey, ci, etwFormat: true);
6511 sb.Append(" <string id=\"").Append(stringKey).Append("\" value=\"").Append(val).Append("\"/>").AppendLine();
6513 sb.Append(" </stringTable>").AppendLine();
6514 sb.Append(" </resources>").AppendLine();
6516 sb.Append("</localization>").AppendLine();
6517 sb.AppendLine("</instrumentationManifest>");
6518 return sb.ToString();
6522 private void WriteNameAndMessageAttribs(StringBuilder stringBuilder, string elementName, string name)
6524 stringBuilder.Append(" name=\"").Append(name).Append("\"");
6525 WriteMessageAttrib(sb, elementName, name, name);
6527 private void WriteMessageAttrib(StringBuilder stringBuilder, string elementName, string name, string value)
6529 string key = elementName + "_" + name;
6530 // See if the user wants things localized.
6531 if (resources != null)
6533 // resource fallback: strings in the neutral culture will take precedence over inline strings
6534 string localizedString = resources.GetString(key, CultureInfo.InvariantCulture);
6535 if (localizedString != null)
6536 value = localizedString;
6541 stringBuilder.Append(" message=\"$(string.").Append(key).Append(")\"");
6543 if (stringTab.TryGetValue(key, out prevValue) && !prevValue.Equals(value))
6545 ManifestError(Environment.GetResourceString("EventSource_DuplicateStringKey", key), true);
6549 stringTab[key] = value;
6551 internal string GetLocalizedMessage(string key, CultureInfo ci, bool etwFormat)
6553 string value = null;
6554 if (resources != null)
6556 string localizedString = resources.GetString(key, ci);
6557 if (localizedString != null)
6559 value = localizedString;
6560 if (etwFormat && key.StartsWith("event_"))
6562 var evtName = key.Substring("event_".Length);
6563 value = TranslateToManifestConvention(value, evtName);
6567 if (etwFormat && value == null)
6568 stringTab.TryGetValue(key, out value);
6574 /// There's no API to enumerate all languages an assembly is localized into, so instead
6575 /// we enumerate through all the "known" cultures and attempt to load a corresponding satellite
6578 /// <param name="resources"></param>
6579 /// <returns></returns>
6580 private static List<CultureInfo> GetSupportedCultures(ResourceManager resources)
6582 var cultures = new List<CultureInfo>();
6583 #if !ES_BUILD_PCL && !FEATURE_CORECLR
6584 foreach (CultureInfo ci in CultureInfo.GetCultures(CultureTypes.SpecificCultures /*| CultureTypes.NeutralCultures*/))
6586 if (resources.GetResourceSet(ci, true, false) != null)
6589 #endif // !ES_BUILD_PCL && !FEATURE_CORECLR
6590 if (!cultures.Contains(CultureInfo.CurrentUICulture))
6591 cultures.Insert(0, CultureInfo.CurrentUICulture);
6595 private static string GetLevelName(EventLevel level)
6597 return (((int)level >= 16) ? "" : "win:") + level.ToString();
6600 #if FEATURE_MANAGED_ETW_CHANNELS
6601 private string GetChannelName(EventChannel channel, string eventName, string eventMessage)
6603 ChannelInfo info = null;
6604 if (channelTab == null || !channelTab.TryGetValue((int)channel, out info))
6606 if (channel < EventChannel.Admin) // || channel > EventChannel.Debug)
6607 ManifestError(Environment.GetResourceString("EventSource_UndefinedChannel", channel, eventName));
6609 // allow channels to be auto-defined. The well known ones get their well known names, and the
6610 // rest get names Channel<N>. This allows users to modify the Manifest if they want more advanced features.
6611 if (channelTab == null)
6612 channelTab = new Dictionary<int, ChannelInfo>(4);
6614 string channelName = channel.ToString(); // For well know channels this is a nice name, otherwise a number
6615 if (EventChannel.Debug < channel)
6616 channelName = "Channel" + channelName; // Add a 'Channel' prefix for numbers.
6618 AddChannel(channelName, (int)channel, GetDefaultChannelAttribute(channel));
6619 if (!channelTab.TryGetValue((int)channel, out info))
6620 ManifestError(Environment.GetResourceString("EventSource_UndefinedChannel", channel, eventName));
6622 // events that specify admin channels *must* have non-null "Message" attributes
6623 if (resources != null && eventMessage == null)
6624 eventMessage = resources.GetString("event_" + eventName, CultureInfo.InvariantCulture);
6625 if (info.Attribs.EventChannelType == EventChannelType.Admin && eventMessage == null)
6626 ManifestError(Environment.GetResourceString("EventSource_EventWithAdminChannelMustHaveMessage", eventName, info.Name));
6630 private string GetTaskName(EventTask task, string eventName)
6632 if (task == EventTask.None)
6636 if (taskTab == null)
6637 taskTab = new Dictionary<int, string>();
6638 if (!taskTab.TryGetValue((int)task, out ret))
6639 ret = taskTab[(int)task] = eventName;
6642 private string GetOpcodeName(EventOpcode opcode, string eventName)
6646 case EventOpcode.Info:
6648 case EventOpcode.Start:
6650 case EventOpcode.Stop:
6652 case EventOpcode.DataCollectionStart:
6653 return "win:DC_Start";
6654 case EventOpcode.DataCollectionStop:
6655 return "win:DC_Stop";
6656 case EventOpcode.Extension:
6657 return "win:Extension";
6658 case EventOpcode.Reply:
6660 case EventOpcode.Resume:
6661 return "win:Resume";
6662 case EventOpcode.Suspend:
6663 return "win:Suspend";
6664 case EventOpcode.Send:
6666 case EventOpcode.Receive:
6667 return "win:Receive";
6671 if (opcodeTab == null || !opcodeTab.TryGetValue((int)opcode, out ret))
6673 ManifestError(Environment.GetResourceString("EventSource_UndefinedOpcode", opcode, eventName), true);
6678 private string GetKeywords(ulong keywords, string eventName)
6681 for (ulong bit = 1; bit != 0; bit <<= 1)
6683 if ((keywords & bit) != 0)
6685 string keyword = null;
6686 if ((keywordTab == null || !keywordTab.TryGetValue(bit, out keyword)) &&
6687 (bit >= (ulong)0x1000000000000))
6689 // do not report Windows reserved keywords in the manifest (this allows the code
6690 // to be resilient to potential renaming of these keywords)
6691 keyword = string.Empty;
6693 if (keyword == null)
6695 ManifestError(Environment.GetResourceString("EventSource_UndefinedKeyword", "0x" + bit.ToString("x", CultureInfo.CurrentCulture), eventName), true);
6696 keyword = string.Empty;
6698 if (ret.Length != 0 && keyword.Length != 0)
6700 ret = ret + keyword;
6705 private string GetTypeName(Type type)
6709 FieldInfo[] fields = type.GetFields(BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance);
6710 var typeName = GetTypeName(fields[0].FieldType);
6711 return typeName.Replace("win:Int", "win:UInt"); // ETW requires enums to be unsigned.
6713 switch (type.GetTypeCode())
6715 case TypeCode.Boolean:
6716 return "win:Boolean";
6720 case TypeCode.UInt16:
6721 return "win:UInt16";
6722 case TypeCode.UInt32:
6723 return "win:UInt32";
6724 case TypeCode.UInt64:
6725 return "win:UInt64";
6726 case TypeCode.SByte:
6728 case TypeCode.Int16:
6730 case TypeCode.Int32:
6732 case TypeCode.Int64:
6734 case TypeCode.String:
6735 return "win:UnicodeString";
6736 case TypeCode.Single:
6738 case TypeCode.Double:
6739 return "win:Double";
6740 case TypeCode.DateTime:
6741 return "win:FILETIME";
6743 if (type == typeof(Guid))
6745 else if (type == typeof(IntPtr))
6746 return "win:Pointer";
6747 else if ((type.IsArray || type.IsPointer) && type.GetElementType() == typeof(byte))
6748 return "win:Binary";
6749 ManifestError(Environment.GetResourceString("EventSource_UnsupportedEventTypeInManifest", type.Name), true);
6750 return string.Empty;
6754 private static void UpdateStringBuilder(ref StringBuilder stringBuilder, string eventMessage, int startIndex, int count)
6756 if (stringBuilder == null)
6757 stringBuilder = new StringBuilder();
6758 stringBuilder.Append(eventMessage, startIndex, count);
6761 // Manifest messages use %N conventions for their message substitutions. Translate from
6762 // .NET conventions. We can't use RegEx for this (we are in mscorlib), so we do it 'by hand'
6763 private string TranslateToManifestConvention(string eventMessage, string evtName)
6765 StringBuilder stringBuilder = null; // We lazily create this
6766 int writtenSoFar = 0;
6770 if (i >= eventMessage.Length)
6772 if (stringBuilder == null)
6773 return eventMessage;
6774 UpdateStringBuilder(ref stringBuilder, eventMessage, writtenSoFar, i - writtenSoFar);
6775 return stringBuilder.ToString();
6778 if (eventMessage[i] == '%')
6780 // handle format message escaping character '%' by escaping it
6781 UpdateStringBuilder(ref stringBuilder, eventMessage, writtenSoFar, i - writtenSoFar);
6782 stringBuilder.Append("%%");
6786 else if (i < eventMessage.Length - 1 &&
6787 (eventMessage[i] == '{' && eventMessage[i + 1] == '{' || eventMessage[i] == '}' && eventMessage[i + 1] == '}'))
6789 // handle C# escaped '{" and '}'
6790 UpdateStringBuilder(ref stringBuilder, eventMessage, writtenSoFar, i - writtenSoFar);
6791 stringBuilder.Append(eventMessage[i]);
6795 else if (eventMessage[i] == '{')
6797 int leftBracket = i;
6800 while (i < eventMessage.Length && Char.IsDigit(eventMessage[i]))
6802 argNum = argNum * 10 + eventMessage[i] - '0';
6805 if (i < eventMessage.Length && eventMessage[i] == '}')
6808 UpdateStringBuilder(ref stringBuilder, eventMessage, writtenSoFar, leftBracket - writtenSoFar);
6809 int manIndex = TranslateIndexToManifestConvention(argNum, evtName);
6810 stringBuilder.Append('%').Append(manIndex);
6811 // An '!' after the insert specifier {n} will be interpreted as a literal.
6812 // We'll escape it so that mc.exe does not attempt to consider it the
6813 // beginning of a format string.
6814 if (i < eventMessage.Length && eventMessage[i] == '!')
6817 stringBuilder.Append("%!");
6823 ManifestError(Environment.GetResourceString("EventSource_UnsupportedMessageProperty", evtName, eventMessage));
6826 else if ((chIdx = "&<>'\"\r\n\t".IndexOf(eventMessage[i])) >= 0)
6828 string[] escapes = { "&", "<", ">", "'", """, "%r", "%n", "%t" };
6829 var update = new Action<char, string>(
6832 UpdateStringBuilder(ref stringBuilder, eventMessage, writtenSoFar, i - writtenSoFar);
6834 stringBuilder.Append(escape);
6837 update(eventMessage[i], escapes[chIdx]);
6844 private int TranslateIndexToManifestConvention(int idx, string evtName)
6846 List<int> byteArrArgIndices;
6847 if (perEventByteArrayArgIndices.TryGetValue(evtName, out byteArrArgIndices))
6849 foreach (var byArrIdx in byteArrArgIndices)
6851 if (idx >= byArrIdx)
6860 #if FEATURE_MANAGED_ETW_CHANNELS
6864 public ulong Keywords;
6865 public EventChannelAttribute Attribs;
6869 Dictionary<int, string> opcodeTab;
6870 Dictionary<int, string> taskTab;
6871 #if FEATURE_MANAGED_ETW_CHANNELS
6872 Dictionary<int, ChannelInfo> channelTab;
6874 Dictionary<ulong, string> keywordTab;
6875 Dictionary<string, Type> mapsTab;
6877 Dictionary<string, string> stringTab; // Maps unlocalized strings to localized ones
6879 #if FEATURE_MANAGED_ETW_CHANNELS
6880 ulong nextChannelKeywordBit = 0x8000000000000000; // available Keyword bit to be used for next channel definition
6881 const int MaxCountChannels = 8; // a manifest can defined at most 8 ETW channels
6884 StringBuilder sb; // Holds the provider information.
6885 StringBuilder events; // Holds the events.
6886 StringBuilder templates;
6888 #if FEATURE_MANAGED_ETW_CHANNELS
6889 string providerName;
6891 ResourceManager resources; // Look up localized strings here.
6892 EventManifestOptions flags;
6893 IList<string> errors; // list of currently encountered errors
6894 Dictionary<string, List<int>> perEventByteArrayArgIndices; // "event_name" -> List_of_Indices_of_Byte[]_Arg
6896 // State we track between StartEvent and EndEvent.
6897 string eventName; // Name of the event currently being processed.
6898 int numParams; // keeps track of the number of args the event has.
6899 List<int> byteArrArgIndices; // keeps track of the index of each byte[] argument
6904 /// Used to send the m_rawManifest into the event dispatcher as a series of events.
6906 internal struct ManifestEnvelope
6908 public const int MaxChunkSize = 0xFF00;
6909 public enum ManifestFormats : byte
6911 SimpleXmlFormat = 1, // simply dump the XML manifest as UTF8
6914 public ManifestFormats Format;
6915 public byte MajorVersion;
6916 public byte MinorVersion;
6918 public ushort TotalChunks;
6919 public ushort ChunkNumber;