[mcs tests] clean up interp target
[mono-project.git] / mcs / class / referencesource / mscorlib / system / diagnostics / eventing / eventproviderbase.cs
blobb1b7786df7e252f1c86a96303144059702326bfa
1 // Copyright (c) Microsoft Corporation. All rights reserved
2 // This program uses code hyperlinks available as part of the HyperAddin Visual Studio plug-in.
3 // It is available from http://www.codeplex.com/hyperAddin
4 #define FEATURE_MANAGED_ETW
6 #if !FEATURE_CORECLR || FEATURE_NETCORE
7 #define FEATURE_ACTIVITYSAMPLING
8 #endif // !FEATURE_CORECLR || FEATURE_NETCORE
10 using System;
11 using System.Collections.Concurrent;
12 using System.Collections.Generic;
13 using System.Collections.ObjectModel;
14 using System.Diagnostics;
15 using System.Diagnostics.CodeAnalysis;
16 using System.Diagnostics.Contracts;
17 using System.Globalization;
18 using System.Reflection;
19 using System.Resources;
20 using System.Security;
21 using System.Security.Permissions;
22 using System.Text;
23 using System.Threading;
24 using Microsoft.Win32;
26 namespace System.Diagnostics.Tracing
28 /// <summary>
29 /// This class is meant to be inherited by a user eventSource (which provides specific events and then
30 /// calls code:EventSource.WriteEvent to log them).
31 ///
32 /// sealed class MinimalEventSource : EventSource
33 /// {
34 /// * public void Load(long ImageBase, string Name) { WriteEvent(1, ImageBase, Name); }
35 /// * public void Unload(long ImageBase) { WriteEvent(2, ImageBase); }
36 /// * private MinimalEventSource() {}
37 /// }
38 ///
39 /// This functionaity is sufficient for many users. When more control is needed over the ETW manifest
40 /// that is created, that can be done by adding [Event] attributes on the methods.
41 ///
42 /// Finally for very advanced EventSources, it is possible to intercept the commands being given to the
43 /// eventSource and change what filtering is done (or cause actions to be performed by the eventSource (eg
44 /// dumping a data structure).
45 ///
46 /// The eventSources can be turned on with Window ETW controllers (eg logman), immediately. It is also
47 /// possible to control and intercept the data dispatcher programatically. We code:EventListener for
48 /// more.
49 /// </summary>
50 public class EventSource : IDisposable
52 /// <summary>
53 /// The human-friendly name of the eventSource. It defaults to the simple name of the class
54 /// </summary>
55 public string Name { get { return m_name; } }
56 /// <summary>
57 /// Every eventSource is assigned a GUID to uniquely identify it to the system.
58 /// </summary>
59 public Guid Guid { get { return m_guid; } }
61 /// <summary>
62 /// Returns true if the eventSource has been enabled at all.
63 /// </summary>
64 [SuppressMessage("Microsoft.Concurrency", "CA8001", Justification = "This does not need to be correct when racing with other threads")]
65 #if !FEATURE_CORECLR
66 [System.Runtime.TargetedPatchingOptOut("Performance critical to inline across NGen image boundaries")]
67 #endif
68 public bool IsEnabled()
70 return m_eventSourceEnabled;
72 /// <summary>
73 /// Returns true if events with >= 'level' and have one of 'keywords' set are enabled.
74 ///
75 /// Note that the result of this function only an approximiation on whether a particular
76 /// event is active or not. It is only meant to be used as way of avoiding expensive
77 /// computation for logging when logging is not on, therefore it sometimes returns false
78 // positives (but is always accurate when returning false). EventSources are free to
79 /// have additional filtering.
80 /// </summary>
81 [SuppressMessage("Microsoft.Concurrency", "CA8001", Justification = "This does not need to be correct when racing with other threads")]
82 public bool IsEnabled(EventLevel level, EventKeywords keywords)
84 if (!m_eventSourceEnabled)
85 return false;
86 if (m_level != 0 && m_level < level)
87 return false;
88 if (m_matchAnyKeyword != 0 && (keywords & m_matchAnyKeyword) == 0)
89 return false;
91 #if !FEATURE_ACTIVITYSAMPLING
93 return true;
95 #else // FEATURE_ACTIVITYSAMPLING
97 return true;
99 #if OPTIMIZE_IS_ENABLED
100 //================================================================================
101 // 2013/03/06 - The code below is a possible optimization for IsEnabled(level, kwd)
102 // in case activity tracing/sampling is enabled. The added complexity of this
103 // code however weighs against having it "on" until we know it's really needed.
104 // For now we'll have this #ifdef-ed out in case we see evidence this is needed.
105 //================================================================================
107 // At this point we believe the event is enabled, however we now need to check
108 // if we filter because of activity
110 // Optimization, all activity filters also register a delegate here, so if there
111 // is no delegate, we know there are no activity filters, which means that there
112 // is no additional filtering, which means that we can return true immediately.
113 if (s_activityDying == null)
114 return true;
116 // if there's at least one legacy ETW listener we can't filter this
117 if (m_legacySessions != null && m_legacySessions.Count > 0)
118 return true;
120 // if any event ID that triggers a new activity, or "transfers" activities
121 // is covered by 'keywords' we can't filter this
122 if (((long)keywords & m_keywordTriggers) != 0)
123 return true;
125 // See if all listeners have activity filters that would block the event.
126 for (int perEventSourceSessionId = 0; perEventSourceSessionId < SessionMask.MAX; ++perEventSourceSessionId)
128 EtwSession etwSession = m_etwSessionIdMap[perEventSourceSessionId];
129 if (etwSession == null)
130 continue;
132 ActivityFilter activityFilter = etwSession.m_activityFilter;
133 if (activityFilter == null ||
134 ActivityFilter.GetFilter(activityFilter, this) == null)
136 // No activity filter for ETW, if event is active for ETW, we can't filter.
137 for (int i = 0; i < m_eventData.Length; i++)
138 if (m_eventData[i].EnabledForETW)
139 return true;
141 else if (ActivityFilter.IsCurrentActivityActive(activityFilter))
142 return true;
145 // for regular event listeners
146 var curDispatcher = m_Dispatchers;
147 while (curDispatcher != null)
149 ActivityFilter activityFilter = curDispatcher.m_Listener.m_activityFilter;
150 if (activityFilter == null)
152 // See if any event is enabled.
153 for (int i = 0; i < curDispatcher.m_EventEnabled.Length; i++)
154 if (curDispatcher.m_EventEnabled[i])
155 return true;
157 else if (ActivityFilter.IsCurrentActivityActive(activityFilter))
158 return true;
159 curDispatcher = curDispatcher.m_Next;
162 // Every listener has an activity filter that is blocking writing the event,
163 // thus the event is not enabled.
164 return false;
165 #endif // OPTIMIZE_IS_ENABLED
167 #endif // FEATURE_ACTIVITYSAMPLING
170 // Manifest support
171 /// <summary>
172 /// Returns the GUID that uniquely identifies the eventSource defined by 'eventSourceType'.
173 /// This API allows you to compute this without actually creating an instance of the EventSource.
174 /// It only needs to reflect over the type.
175 /// </summary>
176 public static Guid GetGuid(Type eventSourceType)
178 if (eventSourceType == null)
179 throw new ArgumentNullException("eventSourceType");
180 Contract.EndContractBlock();
182 EventSourceAttribute attrib = (EventSourceAttribute)GetCustomAttributeHelper(eventSourceType, typeof(EventSourceAttribute));
183 string name = eventSourceType.Name;
184 if (attrib != null)
186 if (attrib.Guid != null)
188 Guid g = Guid.Empty;
189 if(Guid.TryParse(attrib.Guid, out g))
190 return g;
193 if (attrib.Name != null)
194 name = attrib.Name;
197 if (name == null)
198 throw new ArgumentException("eventSourceType", Environment.GetResourceString("Argument_InvalidTypeName"));
200 return GenerateGuidFromName(name.ToUpperInvariant()); // Make it case insensitive.
202 /// <summary>
203 /// Returns the official ETW Provider name for the eventSource defined by 'eventSourceType'.
204 /// This API allows you to compute this without actually creating an instance of the EventSource.
205 /// It only needs to reflect over the type.
206 /// </summary>
207 public static string GetName(Type eventSourceType)
209 if (eventSourceType == null)
210 throw new ArgumentNullException("eventSourceType");
211 Contract.EndContractBlock();
213 EventSourceAttribute attrib = (EventSourceAttribute)GetCustomAttributeHelper(eventSourceType, typeof(EventSourceAttribute));
214 if (attrib != null && attrib.Name != null)
215 return attrib.Name;
217 return eventSourceType.Name;
219 /// <summary>
220 /// Returns a string of the XML manifest associated with the eventSourceType. The scheme for this XML is
221 /// documented at in EventManifest Schema http://msdn.microsoft.com/en-us/library/aa384043(VS.85).aspx
222 /// </summary>
223 /// <param name="assemblyPathForManifest">The manifest XML fragment contains the string name of the DLL name in
224 /// which it is embeded. This parameter spcifies what name will be used</param>
225 /// <returns>The XML data string</returns>
226 public static string GenerateManifest(Type eventSourceType, string assemblyPathToIncludeInManifest)
228 if (eventSourceType == null)
229 throw new ArgumentNullException("eventSourceType");
230 Contract.EndContractBlock();
232 byte[] manifestBytes = EventSource.CreateManifestAndDescriptors(eventSourceType, assemblyPathToIncludeInManifest, null);
233 return Encoding.UTF8.GetString(manifestBytes);
236 // EventListener support
237 /// <summary>
238 /// returns a list (IEnumerable) of all sources in the appdomain). EventListners typically need this.
239 /// </summary>
240 /// <returns></returns>
241 public static IEnumerable<EventSource> GetSources()
243 var ret = new List<EventSource>();
244 lock (EventListener.EventListenersLock)
246 foreach (WeakReference eventSourceRef in EventListener.s_EventSources)
248 EventSource eventSource = eventSourceRef.Target as EventSource;
249 if (eventSource != null)
250 ret.Add(eventSource);
253 return ret;
256 /// <summary>
257 /// Send a command to a particular EventSource identified by 'eventSource'
258 ///
259 /// Calling this routine simply forwards the command to the EventSource.OnEventCommand
260 /// callback. What the EventSource does with the command and its arguments are from that point
261 /// EventSource-specific.
262 ///
263 /// The eventSource is passed the EventListener that issued the command along with the command and
264 /// arguments. The contract is that to the extent possible the eventSource should not affect other
265 /// EventListeners (eg filtering events), however sometimes this simply is not possible (if the
266 /// command was to provoke a GC, or a System flush etc).
267 /// </summary>
268 public static void SendCommand(EventSource eventSource, EventCommand command, IDictionary<string, string> commandArguments)
270 if (eventSource == null)
271 throw new ArgumentNullException("eventSource");
273 // User-defined EventCommands should not conflict with the reserved commands.
274 if ((int)command <= (int)EventCommand.Update && (int)command != (int)EventCommand.SendManifest)
275 throw new ArgumentException("Invalid command value.", "command");
277 eventSource.SendCommand(null, 0, 0, command, true, EventLevel.LogAlways, EventKeywords.None, commandArguments);
280 // ActivityID support (see also WriteEventWithRelatedActivityIdCore)
281 /// <summary>
282 /// When a thread starts work that is on behalf of 'something else' (typically another
283 /// thread or network request) it should mark the thread as working on that other work.
284 /// This API marks the current thread as working on activity 'activityID'. This API
285 /// should be used when the caller knows the thread's current activity (the one being
286 /// overwritten) has completed. Otherwise, callers should prefer the overload that
287 /// return the oldActivityThatWillContinue (below).
288 ///
289 /// All events created with the EventSource on this thread are also tagged with the
290 /// activity ID of the thread.
291 ///
292 /// It is common, and good practice after setting the thread to an activity to log an event
293 /// with a 'start' opcode to indicate that precise time/thread where the new activity
294 /// started.
295 /// </summary>
296 /// <param name="activityId">A Guid that represents the new activity with which to mark
297 /// the current thread</param>
298 [System.Security.SecuritySafeCritical]
299 public static void SetCurrentThreadActivityId(Guid activityId)
301 #if FEATURE_ACTIVITYSAMPLING
302 Guid newId = activityId;
303 #endif // FEATURE_ACTIVITYSAMPLING
304 // We ignore errors to keep with the convention that EventSources do not throw errors.
305 // Note we can't access m_throwOnWrites because this is a static method.
306 if (UnsafeNativeMethods.ManifestEtw.EventActivityIdControl(
307 UnsafeNativeMethods.ManifestEtw.ActivityControl.EVENT_ACTIVITY_CTRL_GET_SET_ID,
308 ref activityId) == 0)
310 #if FEATURE_ACTIVITYSAMPLING
311 var activityDying = s_activityDying;
312 if (activityDying != null && newId != activityId)
314 if (activityId == Guid.Empty)
316 activityId = FallbackActivityId;
318 // OutputDebugString(string.Format("Activity dying: {0} -> {1}", activityId, newId));
319 activityDying(activityId); // This is actually the OLD activity ID.
321 #endif // FEATURE_ACTIVITYSAMPLING
325 /// <summary>
326 /// When a thread starts work that is on behalf of 'something else' (typically another
327 /// thread or network request) it should mark the thread as working on that other work.
328 /// This API marks the current thread as working on activity 'activityID'. It returns
329 /// whatever activity the thread was previously marked with. There is a convention that
330 /// callers can assume that callees restore this activity mark before the callee returns.
331 /// To encourage this this API returns the old activity, so that it can be restored later.
332 ///
333 /// All events created with the EventSource on this thread are also tagged with the
334 /// activity ID of the thread.
335 ///
336 /// It is common, and good practice after setting the thread to an activity to log an event
337 /// with a 'start' opcode to indicate that precise time/thread where the new activity
338 /// started.
339 /// </summary>
340 /// <param name="activityId">A Guid that represents the new activity with which to mark
341 /// the current thread</param>
342 /// <param name="oldActivityThatWillContinue">The Guid that represents the current activity
343 /// which will continue at some point in the future, on the current thread</param>
344 [System.Security.SecuritySafeCritical]
345 public static void SetCurrentThreadActivityId(Guid activityId, out Guid oldActivityThatWillContinue)
347 oldActivityThatWillContinue = activityId;
348 // We ignore errors to keep with the convention that EventSources do not throw errors.
349 // Note we can't access m_throwOnWrites because this is a static method.
350 UnsafeNativeMethods.ManifestEtw.EventActivityIdControl(
351 UnsafeNativeMethods.ManifestEtw.ActivityControl.EVENT_ACTIVITY_CTRL_GET_SET_ID,
352 ref oldActivityThatWillContinue);
353 // We don't call the activityDying callback here because the caller has declared that
354 // it is not dying.
357 public static Guid CurrentThreadActivityId
359 [System.Security.SecurityCritical]
362 // We ignore errors to keep with the convention that EventSources do not throw
363 // errors. Note we can't access m_throwOnWrites because this is a static method.
364 Guid retVal = new Guid();
365 UnsafeNativeMethods.ManifestEtw.EventActivityIdControl(
366 UnsafeNativeMethods.ManifestEtw.ActivityControl.EVENT_ACTIVITY_CTRL_GET_ID,
367 ref retVal);
368 return retVal;
372 /// <summary>
373 /// This property allows EventSource code to appropriately handle as "different"
374 /// activities started on different threads that have not had an activity created on them.
375 /// </summary>
376 internal static Guid InternalCurrentThreadActivityId
378 [System.Security.SecurityCritical]
381 Guid retval = CurrentThreadActivityId;
382 if (retval == Guid.Empty)
384 retval = FallbackActivityId;
386 return retval;
390 internal static Guid FallbackActivityId
392 [System.Security.SecurityCritical]
395 #pragma warning disable 612, 618
396 // Managed thread IDs are more aggressively re-used than native thread IDs,
397 // so we'll use the latter...
398 return new Guid((uint) AppDomain.GetCurrentThreadId(),
399 (ushort) s_currentPid, (ushort) (s_currentPid >> 16),
400 0x94, 0x1b, 0x87, 0xd5, 0xa6, 0x5c, 0x36, 0x64);
401 #pragma warning restore 612, 618
405 // Error APIs. (We don't throw by default, but you can probe for status)
406 /// <summary>
407 /// Because
408 ///
409 /// 1) Logging is often optional and thus should not generate fatal errors (exceptions)
410 /// 2) EventSources are often initialized in class constructors (which propagate exceptions poorly)
411 ///
412 /// The event source constructor does not throw exceptions. Instead we remember any exception that
413 /// was generated (it is also logged to Trace.WriteLine).
414 /// </summary>
415 public Exception ConstructionException { get { return m_constructionException; } }
417 /// <summary>
418 /// Displays thew name and GUID for the eventSoruce for debugging purposes.
419 /// </summary>
420 public override string ToString() { return Environment.GetResourceString("EventSource_ToString", Name, Guid); }
422 #region protected
423 /// <summary>
424 /// This is the consturctor that most users will use to create their eventSource. It takes
425 /// no parameters. The ETW provider name and GUID of the EventSource are determined by the EventSource
426 /// custom attribute (so you can determine these things declaratively). If the GUID for the eventSource
427 /// is not specified in the EventSourceAttribute (recommended), it is Generated by hashing the name.
428 /// If the ETW provider name of the EventSource is not given, the name of the EventSource class is used as
429 /// the ETW provider name.
430 /// </summary>
431 protected EventSource()
432 : this(false)
436 /// <summary>
437 /// By default calling the 'WriteEvent' methods do NOT throw on errors (they silently discard the event).
438 /// This is because in most cases users assume logging is not 'precious' and do NOT wish to have logging falures
439 /// crash the program. However for those applications where logging is 'precious' and if it fails the caller
440 /// wishes to react, setting 'throwOnEventWriteErrors' will cause an exception to be thrown if WriteEvent
441 /// fails. Note the fact that EventWrite succeeds does not necessarily mean that the event reached its destination
442 /// only that operation of writing it did not fail.
443 /// </summary>
444 protected EventSource(bool throwOnEventWriteErrors)
446 m_throwOnEventWriteErrors = throwOnEventWriteErrors;
449 Contract.Assert(m_lastCommandException == null);
450 var myType = this.GetType();
451 Initialize(GetGuid(myType), GetName(myType));
452 m_constructionException = m_lastCommandException;
454 catch (Exception e)
456 Contract.Assert(m_eventData == null && m_eventSourceEnabled == false);
457 ReportOutOfBandMessage("ERROR: Exception during construction of EventSource " + Name + ": "
458 + e.Message, false);
459 m_eventSourceEnabled = false; // This is insurance, it should still be off.
460 if (m_lastCommandException != null)
461 m_constructionException = m_lastCommandException;
462 else
463 m_constructionException = e;
467 // FrameworkEventSource is on the startup path for the framework, so we have this internal overload that it can use
468 // to prevent the working set hit from looking at the custom attributes on the type to get the Guid.
469 internal EventSource(Guid eventSourceGuid, string eventSourceName)
471 Initialize(eventSourceGuid, eventSourceName);
474 [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1720:IdentifiersShouldNotContainTypeNames", MessageId = "guid")]
475 [SecuritySafeCritical]
476 private void Initialize(Guid eventSourceGuid, string eventSourceName)
478 if (eventSourceGuid == Guid.Empty)
480 throw new ArgumentException(Environment.GetResourceString("EventSource_NeedGuid"));
483 if (eventSourceName == null)
485 throw new ArgumentException(Environment.GetResourceString("EventSource_NeedName"));
488 m_name = eventSourceName;
489 m_guid = eventSourceGuid;
490 #if FEATURE_ACTIVITYSAMPLING
491 m_curLiveSessions = new SessionMask(0);
492 m_etwSessionIdMap = new EtwSession[SessionMask.MAX];
493 #endif // FEATURE_ACTIVITYSAMPLING
495 #if FEATURE_MANAGED_ETW
496 m_provider = new OverideEventProvider(this);
500 m_provider.Register(eventSourceGuid);
502 catch (ArgumentException)
504 // Failed to register. Don't crash the app, just don't write events to ETW.
505 m_provider = null;
507 #endif
508 // Add the eventSource to the global (weak) list. This also sets m_id, which is the
509 // index in the list.
510 EventListener.AddEventSource(this);
512 // We are logically completely initialized at this point.
513 m_completelyInited = true;
515 // report any possible errors
516 ReportOutOfBandMessage(null, true);
518 #if FEATURE_ACTIVITYSAMPLING
519 // we cue sending sampling info here based on whether we had to defer sending
520 // the manifest
521 // note: we do *not* send sampling info to any EventListeners because
522 // the following common code pattern would cause an AV:
523 // class MyEventSource: EventSource
524 // {
525 // public static EventSource Log;
526 // }
527 // class MyEventListener: EventListener
528 // {
529 // protected override void OnEventWritten(...)
530 // { MyEventSource.Log.anything; } <-- AV, as the static Log was not set yet
531 // }
532 if (m_eventSourceEnabled && m_deferedSendManifest)
533 ReportActivitySamplingInfo(null, m_curLiveSessions);
534 #endif // FEATURE_ACTIVITYSAMPLING
536 // If we are active and we have not sent our manifest, do so now.
537 if (m_eventSourceEnabled && m_deferedSendManifest)
538 SendManifest(m_rawManifest);
541 /// <summary>
542 /// This method is called when the eventSource is updated by the controller.
543 /// </summary>
544 protected virtual void OnEventCommand(EventCommandEventArgs command) { }
546 internal void WriteString(string msg, SessionMask m)
548 if (m_eventSourceEnabled)
550 WriteEventString(0, (long) m.ToEventKeywords(), msg);
551 WriteStringToAllListeners(msg);
554 internal void WriteString(string msg)
556 WriteString(msg, SessionMask.All);
559 internal void WriteStringToListener(EventListener listener, string msg, SessionMask m)
561 Contract.Assert(listener == null || (uint)m == (uint)SessionMask.FromId(0));
563 if (m_eventSourceEnabled)
565 if (listener == null)
567 WriteEventString(0, (long) m.ToEventKeywords(), msg);
569 else
571 List<object> arg = new List<object>();
572 arg.Add(msg);
573 EventWrittenEventArgs eventCallbackArgs = new EventWrittenEventArgs(this);
574 eventCallbackArgs.EventId = 0;
575 eventCallbackArgs.Payload = new ReadOnlyCollection<object>(arg);
576 listener.OnEventWritten(eventCallbackArgs);
581 // optimized for common signatures (no args)
582 [SecuritySafeCritical]
583 [SuppressMessage("Microsoft.Concurrency", "CA8001", Justification = "This does not need to be correct when racing with other threads")]
584 protected unsafe void WriteEvent(int eventId)
586 WriteEventCore(eventId, 0, null);
589 // optimized for common signatures (ints)
590 [SecuritySafeCritical]
591 [SuppressMessage("Microsoft.Concurrency", "CA8001", Justification = "This does not need to be correct when racing with other threads")]
592 protected unsafe void WriteEvent(int eventId, int arg1)
594 if (m_eventSourceEnabled)
596 EventSource.EventData* descrs = stackalloc EventSource.EventData[1];
597 descrs[0].DataPointer = (IntPtr)(&arg1);
598 descrs[0].Size = 4;
599 WriteEventCore(eventId, 1, descrs);
603 [SecuritySafeCritical]
604 [SuppressMessage("Microsoft.Concurrency", "CA8001", Justification = "This does not need to be correct when racing with other threads")]
605 protected unsafe void WriteEvent(int eventId, int arg1, int arg2)
607 if (m_eventSourceEnabled)
609 EventSource.EventData* descrs = stackalloc EventSource.EventData[2];
610 descrs[0].DataPointer = (IntPtr)(&arg1);
611 descrs[0].Size = 4;
612 descrs[1].DataPointer = (IntPtr)(&arg2);
613 descrs[1].Size = 4;
614 WriteEventCore(eventId, 2, descrs);
618 [SecuritySafeCritical]
619 [SuppressMessage("Microsoft.Concurrency", "CA8001", Justification = "This does not need to be correct when racing with other threads")]
620 protected unsafe void WriteEvent(int eventId, int arg1, int arg2, int arg3)
622 if (m_eventSourceEnabled)
624 EventSource.EventData* descrs = stackalloc EventSource.EventData[3];
625 descrs[0].DataPointer = (IntPtr)(&arg1);
626 descrs[0].Size = 4;
627 descrs[1].DataPointer = (IntPtr)(&arg2);
628 descrs[1].Size = 4;
629 descrs[2].DataPointer = (IntPtr)(&arg3);
630 descrs[2].Size = 4;
631 WriteEventCore(eventId, 3, descrs);
635 // optimized for common signatures (longs)
636 [SecuritySafeCritical]
637 [SuppressMessage("Microsoft.Concurrency", "CA8001", Justification = "This does not need to be correct when racing with other threads")]
638 protected unsafe void WriteEvent(int eventId, long arg1)
640 if (m_eventSourceEnabled)
642 EventSource.EventData* descrs = stackalloc EventSource.EventData[1];
643 descrs[0].DataPointer = (IntPtr)(&arg1);
644 descrs[0].Size = 8;
645 WriteEventCore(eventId, 1, descrs);
649 [SecuritySafeCritical]
650 [SuppressMessage("Microsoft.Concurrency", "CA8001", Justification = "This does not need to be correct when racing with other threads")]
651 protected unsafe void WriteEvent(int eventId, long arg1, long arg2)
653 if (m_eventSourceEnabled)
655 EventSource.EventData* descrs = stackalloc EventSource.EventData[2];
656 descrs[0].DataPointer = (IntPtr)(&arg1);
657 descrs[0].Size = 8;
658 descrs[1].DataPointer = (IntPtr)(&arg2);
659 descrs[1].Size = 8;
660 WriteEventCore(eventId, 2, descrs);
664 [SecuritySafeCritical]
665 [SuppressMessage("Microsoft.Concurrency", "CA8001", Justification = "This does not need to be correct when racing with other threads")]
666 protected unsafe void WriteEvent(int eventId, long arg1, long arg2, long arg3)
668 if (m_eventSourceEnabled)
670 EventSource.EventData* descrs = stackalloc EventSource.EventData[3];
671 descrs[0].DataPointer = (IntPtr)(&arg1);
672 descrs[0].Size = 8;
673 descrs[1].DataPointer = (IntPtr)(&arg2);
674 descrs[1].Size = 8;
675 descrs[2].DataPointer = (IntPtr)(&arg3);
676 descrs[2].Size = 8;
677 WriteEventCore(eventId, 3, descrs);
681 // optimized for common signatures (strings)
682 [SecuritySafeCritical]
683 [SuppressMessage("Microsoft.Concurrency", "CA8001", Justification = "This does not need to be correct when racing with other threads")]
684 protected unsafe void WriteEvent(int eventId, string arg1)
686 if (m_eventSourceEnabled)
688 if (arg1 == null) arg1 = "";
689 fixed (char* string1Bytes = arg1)
691 EventSource.EventData* descrs = stackalloc EventSource.EventData[1];
692 descrs[0].DataPointer = (IntPtr)string1Bytes;
693 descrs[0].Size = ((arg1.Length + 1) * 2);
694 WriteEventCore(eventId, 1, descrs);
699 [SecuritySafeCritical]
700 [SuppressMessage("Microsoft.Concurrency", "CA8001", Justification = "This does not need to be correct when racing with other threads")]
701 protected unsafe void WriteEvent(int eventId, string arg1, string arg2)
703 if (m_eventSourceEnabled)
705 if (arg1 == null) arg1 = "";
706 if (arg2 == null) arg2 = "";
707 fixed (char* string1Bytes = arg1)
708 fixed (char* string2Bytes = arg2)
710 EventSource.EventData* descrs = stackalloc EventSource.EventData[2];
711 descrs[0].DataPointer = (IntPtr)string1Bytes;
712 descrs[0].Size = ((arg1.Length + 1) * 2);
713 descrs[1].DataPointer = (IntPtr)string2Bytes;
714 descrs[1].Size = ((arg2.Length + 1) * 2);
715 WriteEventCore(eventId, 2, descrs);
720 [SecuritySafeCritical]
721 [SuppressMessage("Microsoft.Concurrency", "CA8001", Justification = "This does not need to be correct when racing with other threads")]
722 protected unsafe void WriteEvent(int eventId, string arg1, string arg2, string arg3)
724 if (m_eventSourceEnabled)
726 if (arg1 == null) arg1 = "";
727 if (arg2 == null) arg2 = "";
728 if (arg3 == null) arg3 = "";
729 fixed (char* string1Bytes = arg1)
730 fixed (char* string2Bytes = arg2)
731 fixed (char* string3Bytes = arg3)
733 EventSource.EventData* descrs = stackalloc EventSource.EventData[3];
734 descrs[0].DataPointer = (IntPtr)string1Bytes;
735 descrs[0].Size = ((arg1.Length + 1) * 2);
736 descrs[1].DataPointer = (IntPtr)string2Bytes;
737 descrs[1].Size = ((arg2.Length + 1) * 2);
738 descrs[2].DataPointer = (IntPtr)string3Bytes;
739 descrs[2].Size = ((arg3.Length + 1) * 2);
740 WriteEventCore(eventId, 3, descrs);
745 // optimized for common signatures (string and ints)
746 [SecuritySafeCritical]
747 [SuppressMessage("Microsoft.Concurrency", "CA8001", Justification = "This does not need to be correct when racing with other threads")]
748 protected unsafe void WriteEvent(int eventId, string arg1, int arg2)
750 if (m_eventSourceEnabled)
752 if (arg1 == null) arg1 = "";
753 fixed (char* string1Bytes = arg1)
755 EventSource.EventData* descrs = stackalloc EventSource.EventData[2];
756 descrs[0].DataPointer = (IntPtr)string1Bytes;
757 descrs[0].Size = ((arg1.Length + 1) * 2);
758 descrs[1].DataPointer = (IntPtr)(&arg2);
759 descrs[1].Size = 4;
760 WriteEventCore(eventId, 2, descrs);
765 [SecuritySafeCritical]
766 [SuppressMessage("Microsoft.Concurrency", "CA8001", Justification = "This does not need to be correct when racing with other threads")]
767 protected unsafe void WriteEvent(int eventId, string arg1, int arg2, int arg3)
769 if (m_eventSourceEnabled)
771 if (arg1 == null) arg1 = "";
772 fixed (char* string1Bytes = arg1)
774 EventSource.EventData* descrs = stackalloc EventSource.EventData[3];
775 descrs[0].DataPointer = (IntPtr)string1Bytes;
776 descrs[0].Size = ((arg1.Length + 1) * 2);
777 descrs[1].DataPointer = (IntPtr)(&arg2);
778 descrs[1].Size = 4;
779 descrs[2].DataPointer = (IntPtr)(&arg3);
780 descrs[2].Size = 4;
781 WriteEventCore(eventId, 3, descrs);
786 // optimized for common signatures (string and longs)
787 [SecuritySafeCritical]
788 [SuppressMessage("Microsoft.Concurrency", "CA8001", Justification = "This does not need to be correct when racing with other threads")]
789 protected unsafe void WriteEvent(int eventId, string arg1, long arg2)
791 if (m_eventSourceEnabled)
793 if (arg1 == null) arg1 = "";
794 fixed (char* string1Bytes = arg1)
796 EventSource.EventData* descrs = stackalloc EventSource.EventData[2];
797 descrs[0].DataPointer = (IntPtr)string1Bytes;
798 descrs[0].Size = ((arg1.Length + 1) * 2);
799 descrs[1].DataPointer = (IntPtr)(&arg2);
800 descrs[1].Size = 8;
801 WriteEventCore(eventId, 2, descrs);
806 protected internal struct EventData
808 public IntPtr DataPointer { get { return (IntPtr)m_Ptr; } set { m_Ptr = (long)value; } }
809 public int Size { get { return m_Size; } set { m_Size = value; } }
811 #region private
812 //Important, we pass this structure directly to the Win32 EventWrite API, so this structure must be layed out exactly
813 // the way EventWrite wants it.
814 private long m_Ptr;
815 private int m_Size;
816 internal int m_Reserved; // Used to pad the size to match the Win32 API
817 #endregion
820 /// <summary>
821 /// This routine allows you to create efficient WriteEvent helpers, however the code that you use to
822 /// do this while straightfoward is unsafe. See the bodies of the WriteEvent helpers above for its use.
823 /// </summary>
824 [SecurityCritical]
825 [CLSCompliant(false)]
826 protected unsafe void WriteEventCore(int eventId, int eventDataCount, EventSource.EventData* data)
828 WriteEventWithRelatedActivityIdCore(eventId, null, eventDataCount, data);
831 /// <summary>
832 /// This routine allows you to create efficient WriteEventCreatingChildActivity helpers, however the code
833 /// that you use to do this while straightfoward is unsafe. See the bodies of the WriteEvent helpers above
834 /// for its use. The only difference is that you pass the ChildAcivityID from caller through to this API
835 /// </summary>
836 [SecurityCritical]
837 [CLSCompliant(false)]
838 protected unsafe void WriteEventWithRelatedActivityIdCore(int eventId, Guid* childActivityID, int eventDataCount, EventSource.EventData* data)
840 if (m_eventSourceEnabled)
842 Contract.Assert(m_eventData != null); // You must have initialized this if you enabled the source.
843 if (childActivityID != null)
844 ValidateEventOpcodeForTransfer(ref m_eventData[eventId]);
846 #if FEATURE_MANAGED_ETW
847 if (m_eventData[eventId].EnabledForETW)
849 #if FEATURE_ACTIVITYSAMPLING
850 // this code should be kept in [....] with WriteEventVarargs().
851 SessionMask etwSessions = SessionMask.All;
852 // only compute etwSessions if there are *any* ETW filters enabled...
853 if ((ulong)m_curLiveSessions != 0)
854 etwSessions = GetEtwSessionMask(eventId, childActivityID);
855 // OutputDebugString(string.Format("{0}.WriteEvent(id {1}) -> to sessions {2:x}",
856 // m_name, m_eventData[eventId].Name, (ulong) etwSessions));
858 if ((ulong)etwSessions != 0 || m_legacySessions != null && m_legacySessions.Count > 0)
860 if (etwSessions.IsEqualOrSupersetOf(m_curLiveSessions))
862 // OutputDebugString(string.Format(" (1) id {0}, kwd {1:x}",
863 // m_eventData[eventId].Name, m_eventData[eventId].Descriptor.Keywords));
864 // by default the Descriptor.Keyword will have the perEventSourceSessionId bit
865 // mask set to 0x0f so, when all ETW sessions want the event we don't need to
866 // synthesize a new one
867 if (!m_provider.WriteEvent(ref m_eventData[eventId].Descriptor, childActivityID, eventDataCount, (IntPtr)data))
868 ThrowEventSourceException();
870 else
872 long origKwd = (long)((ulong) m_eventData[eventId].Descriptor.Keywords & ~(SessionMask.All.ToEventKeywords()));
873 // OutputDebugString(string.Format(" (2) id {0}, kwd {1:x}",
874 // m_eventData[eventId].Name, etwSessions.ToEventKeywords() | (ulong) origKwd));
875 // only some of the ETW sessions will receive this event. Synthesize a new
876 // Descriptor whose Keywords field will have the appropriate bits set.
877 // etwSessions might be 0, if there are legacy ETW listeners that want this event
878 var desc = new System.Diagnostics.Tracing.EventDescriptor(
879 m_eventData[eventId].Descriptor.EventId,
880 m_eventData[eventId].Descriptor.Version,
881 m_eventData[eventId].Descriptor.Channel,
882 m_eventData[eventId].Descriptor.Level,
883 m_eventData[eventId].Descriptor.Opcode,
884 m_eventData[eventId].Descriptor.Task,
885 (long) etwSessions.ToEventKeywords() | origKwd);
887 if (!m_provider.WriteEvent(ref desc, childActivityID, eventDataCount, (IntPtr)data))
888 ThrowEventSourceException();
891 #else
892 if (!m_provider.WriteEvent(ref m_eventData[eventId].Descriptor, childActivityID, eventDataCount, (IntPtr)data))
893 ThrowEventSourceException();
894 #endif // FEATURE_ACTIVITYSAMPLING
896 #endif
897 if (m_Dispatchers != null && m_eventData[eventId].EnabledForAnyListener)
898 WriteToAllListeners(eventId, childActivityID, eventDataCount, data);
902 // fallback varags helpers.
903 /// <summary>
904 /// This is the varargs helper for writing an event. It does create an array and box all the arguments so it is
905 /// relatively inefficient and should only be used for relatively rare events (e.g. less than 100 / sec). If you
906 /// rates are fast than that you should be used WriteEventCore to create fast helpers for your particular method
907 /// signature. Even if you use this for rare evnets, this call should be guarded by a 'IsEnabled()' check so that
908 /// the varargs call is not made when the EventSource is not active.
909 /// </summary>
910 [SecuritySafeCritical]
911 [SuppressMessage("Microsoft.Concurrency", "CA8001", Justification = "This does not need to be correct when racing with other threads")]
912 protected unsafe void WriteEvent(int eventId, params object[] args)
914 WriteEventVarargs(eventId, null, args);
917 /// <summary>
918 /// This is the varargs helper for writing an event which also creates a child activity. It is completely ----ygous
919 /// to cooresponding WriteEvent (they share implementation). It does create an array and box all the arguments so it is
920 /// relatively inefficient and should only be used for relatively rare events (e.g. less than 100 / sec). If you
921 /// rates are fast than that you should be used WriteEventCore to create fast helpers for your particular method
922 /// signature. Even if you use this for rare evnets, this call should be guarded by a 'IsEnabled()' check so that
923 /// the varargs call is not made when the EventSource is not active.
924 /// </summary>
925 [SecuritySafeCritical]
926 protected unsafe void WriteEventWithRelatedActivityId(int eventId, Guid childActivityID, params object[] args)
928 WriteEventVarargs(eventId, &childActivityID, args);
931 #endregion
933 #region IDisposable Members
934 public void Dispose()
936 this.Dispose(true);
937 GC.SuppressFinalize(this);
939 /// <summary>
940 /// Disposes of an EventSource.
941 /// </summary>
942 /// <remarks>
943 /// Called from Dispose() with disposing=true, and from the finalizer (~MeasurementBlock) with disposing=false.
944 /// Guidelines:
945 /// 1. We may be called more than once: do nothing after the first call.
946 /// 2. Avoid throwing exceptions if disposing is false, i.e. if we're being finalized.
947 /// </remarks>
948 /// <param name="disposing">True if called from Dispose(), false if called from the finalizer.</param>
949 protected virtual void Dispose(bool disposing)
951 if (disposing)
953 #if FEATURE_MANAGED_ETW
954 if (m_provider != null)
956 m_provider.Dispose();
957 m_provider = null;
959 #endif
962 ~EventSource()
964 this.Dispose(false);
966 #endregion
968 #region private
969 private static Guid GenerateGuidFromName(string name)
971 // The algorithm below is following the guidance of http://www.ietf.org/rfc/rfc4122.txt
972 // Create a blob containing a 16 byte number representing the namespace
973 // followed by the unicode bytes in the name.
974 var bytes = new byte[name.Length * 2 + 16];
975 uint namespace1 = 0x482C2DB2;
976 uint namespace2 = 0xC39047c8;
977 uint namespace3 = 0x87F81A15;
978 uint namespace4 = 0xBFC130FB;
979 // Write the bytes most-significant byte first.
980 for (int i = 3; 0 <= i; --i)
982 bytes[i] = (byte)namespace1;
983 namespace1 >>= 8;
984 bytes[i + 4] = (byte)namespace2;
985 namespace2 >>= 8;
986 bytes[i + 8] = (byte)namespace3;
987 namespace3 >>= 8;
988 bytes[i + 12] = (byte)namespace4;
989 namespace4 >>= 8;
991 // Write out the name, most significant byte first
992 for (int i = 0; i < name.Length; i++)
994 bytes[2 * i + 16 + 1] = (byte)name[i];
995 bytes[2 * i + 16] = (byte)(name[i] >> 8);
998 // Compute the Sha1 hash
999 var sha1 = System.Security.Cryptography.SHA1.Create();
1000 byte[] hash = sha1.ComputeHash(bytes);
1002 // Create a GUID out of the first 16 bytes of the hash (SHA-1 create a 20 byte hash)
1003 int a = (((((hash[3] << 8) + hash[2]) << 8) + hash[1]) << 8) + hash[0];
1004 short b = (short)((hash[5] << 8) + hash[4]);
1005 short c = (short)((hash[7] << 8) + hash[6]);
1007 c = (short)((c & 0x0FFF) | 0x5000); // Set high 4 bits of octet 7 to 5, as per RFC 4122
1008 Guid guid = new Guid(a, b, c, hash[8], hash[9], hash[10], hash[11], hash[12], hash[13], hash[14], hash[15]);
1009 return guid;
1012 [SecurityCritical]
1013 private unsafe object DecodeObject(int eventId, int parameterId, IntPtr dataPointer)
1015 Type dataType = m_eventData[eventId].Parameters[parameterId].ParameterType;
1017 Again:
1018 if (dataType == typeof(IntPtr))
1020 return *((IntPtr*)dataPointer);
1022 else if (dataType == typeof(int))
1024 return *((int*)dataPointer);
1026 else if (dataType == typeof(uint))
1028 return *((uint*)dataPointer);
1030 else if (dataType == typeof(long))
1032 return *((long*)dataPointer);
1034 else if (dataType == typeof(ulong))
1036 return *((ulong*)dataPointer);
1038 else if (dataType == typeof(byte))
1040 return *((byte*)dataPointer);
1042 else if (dataType == typeof(sbyte))
1044 return *((sbyte*)dataPointer);
1046 else if (dataType == typeof(short))
1048 return *((short*)dataPointer);
1050 else if (dataType == typeof(ushort))
1052 return *((ushort*)dataPointer);
1054 else if (dataType == typeof(float))
1056 return *((float*)dataPointer);
1058 else if (dataType == typeof(double))
1060 return *((double*)dataPointer);
1062 else if (dataType == typeof(decimal))
1064 return *((decimal*)dataPointer);
1066 else if (dataType == typeof(bool))
1068 // The manifest defines a bool as a 32bit type (WIN32 BOOL), not 1 bit as CLR Does.
1069 if (*((int*)dataPointer) == 1)
1071 return true;
1073 else
1075 return false;
1078 else if (dataType == typeof(Guid))
1080 return *((Guid*)dataPointer);
1082 else if (dataType == typeof(char))
1084 return *((char*)dataPointer);
1086 else if (dataType == typeof(DateTime))
1088 long dateTimeTicks = *((long*)dataPointer);
1089 return DateTime.FromFileTimeUtc(dateTimeTicks);
1091 else
1093 if (dataType.IsEnum)
1095 dataType = Enum.GetUnderlyingType(dataType);
1096 goto Again;
1099 // Everything else is marshaled as a string.
1100 // ETW strings are NULL-terminated, so marshal everything up to the first
1101 // null in the string.
1102 return System.Runtime.InteropServices.Marshal.PtrToStringUni(dataPointer);
1106 // Finds the Dispatcher (which holds the filtering state), for a given dispatcher for the current
1107 // eventSource).
1108 private EventDispatcher GetDispatcher(EventListener listener)
1110 EventDispatcher dispatcher = m_Dispatchers;
1111 while (dispatcher != null)
1113 if (dispatcher.m_Listener == listener)
1114 return dispatcher;
1115 dispatcher = dispatcher.m_Next;
1117 return dispatcher;
1120 [SecurityCritical]
1121 private unsafe void WriteEventVarargs(int eventId, Guid* childActivityID, object[] args)
1123 if (m_eventSourceEnabled)
1125 Contract.Assert(m_eventData != null); // You must have initialized this if you enabled the source.
1126 if (childActivityID != null)
1127 ValidateEventOpcodeForTransfer(ref m_eventData[eventId]);
1129 #if FEATURE_MANAGED_ETW
1130 if (m_eventData[eventId].EnabledForETW)
1132 #if FEATURE_ACTIVITYSAMPLING
1133 // this code should be kept in [....] with WriteEventWithRelatedActivityIdCore().
1134 SessionMask etwSessions = SessionMask.All;
1135 // only compute etwSessions if there are *any* ETW filters enabled...
1136 if ((ulong)m_curLiveSessions != 0)
1137 etwSessions = GetEtwSessionMask(eventId, childActivityID);
1139 if ((ulong)etwSessions != 0 || m_legacySessions != null && m_legacySessions.Count > 0)
1141 if (etwSessions.IsEqualOrSupersetOf(m_curLiveSessions))
1143 // by default the Descriptor.Keyword will have the perEventSourceSessionId bit
1144 // mask set to 0x0f so, when all ETW sessions want the event we don't need to
1145 // synthesize a new one
1146 if (!m_provider.WriteEvent(ref m_eventData[eventId].Descriptor, childActivityID, args))
1147 ThrowEventSourceException();
1149 else
1151 long origKwd = (long)((ulong) m_eventData[eventId].Descriptor.Keywords & ~(SessionMask.All.ToEventKeywords()));
1152 // only some of the ETW sessions will receive this event. Synthesize a new
1153 // Descriptor whose Keywords field will have the appropriate bits set.
1154 var desc = new System.Diagnostics.Tracing.EventDescriptor(
1155 m_eventData[eventId].Descriptor.EventId,
1156 m_eventData[eventId].Descriptor.Version,
1157 m_eventData[eventId].Descriptor.Channel,
1158 m_eventData[eventId].Descriptor.Level,
1159 m_eventData[eventId].Descriptor.Opcode,
1160 m_eventData[eventId].Descriptor.Task,
1161 (long)(ulong)etwSessions | origKwd);
1163 if (!m_provider.WriteEvent(ref desc, childActivityID, args))
1164 ThrowEventSourceException();
1167 #else
1168 if (!m_provider.WriteEvent(ref m_eventData[eventId].Descriptor, childActivityID, args))
1169 ThrowEventSourceException();
1170 #endif // FEATURE_ACTIVITYSAMPLING
1172 #endif // FEATURE_MANAGED_ETW
1173 if (m_Dispatchers != null && m_eventData[eventId].EnabledForAnyListener)
1174 WriteToAllListeners(eventId, childActivityID, args);
1178 [SecurityCritical]
1179 unsafe private void WriteToAllListeners(int eventId, Guid* childActivityID, int eventDataCount, EventSource.EventData* data)
1181 object[] args = new object[eventDataCount];
1183 for (int i = 0; i < eventDataCount; i++)
1184 args[i] = DecodeObject(eventId, i, data[i].DataPointer);
1185 WriteToAllListeners(eventId, childActivityID, args);
1188 // helper for writing to all EventListeners attached the current eventSource.
1189 [SecurityCritical]
1190 unsafe private void WriteToAllListeners(int eventId, Guid* childActivityID, params object[] args)
1192 EventWrittenEventArgs eventCallbackArgs = new EventWrittenEventArgs(this);
1193 eventCallbackArgs.EventId = eventId;
1194 if (childActivityID != null)
1195 eventCallbackArgs.RelatedActivityId = *childActivityID;
1196 eventCallbackArgs.Payload = new ReadOnlyCollection<object>(new List<object>(args));
1198 Exception lastThrownException = null;
1199 for (EventDispatcher dispatcher = m_Dispatchers; dispatcher != null; dispatcher = dispatcher.m_Next)
1201 if (dispatcher.m_EventEnabled[eventId])
1203 #if FEATURE_ACTIVITYSAMPLING
1204 var activityFilter = dispatcher.m_Listener.m_activityFilter;
1205 // order below is important as PassesActivityFilter will "flow" active activities
1206 // even when the current EventSource doesn't have filtering enabled. This allows
1207 // interesting activities to be updated so that sources that do sample can get
1208 // accurate data
1209 if (activityFilter == null ||
1210 ActivityFilter.PassesActivityFilter(activityFilter, childActivityID,
1211 m_eventData[eventId].TriggersActivityTracking > 0,
1212 this, eventId) ||
1213 !dispatcher.m_activityFilteringEnabled)
1214 #endif // FEATURE_ACTIVITYSAMPLING
1218 dispatcher.m_Listener.OnEventWritten(eventCallbackArgs);
1220 catch (Exception e)
1222 ReportOutOfBandMessage("ERROR: Exception during EventSource.OnEventWritten: "
1223 + e.Message, false);
1224 lastThrownException = e;
1230 if (lastThrownException != null)
1232 throw new EventSourceException(lastThrownException);
1236 [SecuritySafeCritical]
1237 [SuppressMessage("Microsoft.Concurrency", "CA8001", Justification = "This does not need to be correct when racing with other threads")]
1238 private unsafe void WriteEventString(EventLevel level, long keywords, string msg)
1240 if (m_eventSourceEnabled)
1242 if (m_provider != null && !m_provider.WriteEventString(level, keywords, msg))
1244 ThrowEventSourceException();
1249 private void WriteStringToAllListeners(string msg)
1251 EventWrittenEventArgs eventCallbackArgs = new EventWrittenEventArgs(this);
1252 eventCallbackArgs.EventId = 0;
1253 eventCallbackArgs.Message = msg;
1255 Exception lastThrownException = null;
1256 for (EventDispatcher dispatcher = m_Dispatchers; dispatcher != null; dispatcher = dispatcher.m_Next)
1258 // if there's *any* enabled event on the dispatcher we'll write out the string
1259 // otherwise we'll treat the listener as disabled and skip it
1260 bool dispatcherEnabled = false;
1261 for (int evtId = 0; evtId < dispatcher.m_EventEnabled.Length; ++evtId)
1263 if (dispatcher.m_EventEnabled[evtId])
1265 dispatcherEnabled = true;
1266 break;
1271 if (dispatcherEnabled)
1272 dispatcher.m_Listener.OnEventWritten(eventCallbackArgs);
1274 catch (Exception e)
1276 lastThrownException = e;
1279 if (lastThrownException != null)
1281 throw new EventSourceException(lastThrownException);
1285 #if FEATURE_ACTIVITYSAMPLING
1286 [SecurityCritical]
1287 unsafe private SessionMask GetEtwSessionMask(int eventId, Guid* childActivityID)
1289 SessionMask etwSessions = new SessionMask();
1291 for (int i = 0; i < SessionMask.MAX; ++i)
1293 EtwSession etwSession = m_etwSessionIdMap[i];
1294 if (etwSession != null)
1296 ActivityFilter activityFilter = etwSession.m_activityFilter;
1297 // PassesActivityFilter() will flow "interesting" activities, so make sure
1298 // to perform this test first, before ORing with ~m_activityFilteringForETWEnabled
1299 // (note: the first test for !m_activityFilteringForETWEnabled[i] ensures we
1300 // do not fire events indiscriminately, when no filters are specified, but only
1301 // if, in addition, the session did not also enable ActivitySampling)
1302 if (activityFilter == null && !m_activityFilteringForETWEnabled[i] ||
1303 activityFilter != null &&
1304 ActivityFilter.PassesActivityFilter(activityFilter, childActivityID,
1305 m_eventData[eventId].TriggersActivityTracking > 0, this, eventId) ||
1306 !m_activityFilteringForETWEnabled[i])
1308 etwSessions[i] = true;
1312 // flow "interesting" activities for all legacy sessions in which there's some
1313 // level of activity tracing enabled (even other EventSources)
1314 if (m_legacySessions != null && m_legacySessions.Count > 0 &&
1315 (EventOpcode)m_eventData[eventId].Descriptor.Opcode == EventOpcode.Send)
1317 // only calculate InternalCurrentThreadActivityId once
1318 Guid *pCurrentActivityId = null;
1319 Guid currentActivityId;
1320 foreach (var legacyEtwSession in m_legacySessions)
1322 if (legacyEtwSession == null)
1323 continue;
1325 ActivityFilter activityFilter = legacyEtwSession.m_activityFilter;
1326 if (activityFilter != null)
1328 if (pCurrentActivityId == null)
1330 currentActivityId = InternalCurrentThreadActivityId;
1331 pCurrentActivityId = &currentActivityId;
1333 ActivityFilter.FlowActivityIfNeeded(activityFilter, pCurrentActivityId, childActivityID);
1338 return etwSessions;
1340 #endif // FEATURE_ACTIVITYSAMPLING
1342 /// <summary>
1343 /// Returns true if 'eventNum' is enabled if you only consider the level and matchAnyKeyword filters.
1344 /// It is possible that eventSources turn off the event based on additional filtering criteria.
1345 /// </summary>
1346 private bool IsEnabledByDefault(int eventNum, bool enable, EventLevel currentLevel, EventKeywords currentMatchAnyKeyword)
1348 if (!enable)
1349 return false;
1351 EventLevel eventLevel = (EventLevel)m_eventData[eventNum].Descriptor.Level;
1352 EventKeywords eventKeywords = (EventKeywords)((ulong)m_eventData[eventNum].Descriptor.Keywords & (~(SessionMask.All.ToEventKeywords())));
1354 if ((eventLevel <= currentLevel) || (currentLevel == 0))
1356 if ((eventKeywords == 0) || ((eventKeywords & currentMatchAnyKeyword) != 0))
1357 return true;
1359 return false;
1362 [System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.NoInlining)]
1363 private void ThrowEventSourceException()
1365 // Only throw an error if we asked for them.
1366 if (m_throwOnEventWriteErrors)
1369 switch (EventProvider.GetLastWriteEventError())
1371 case EventProvider.WriteEventErrorCode.EventTooBig:
1372 ReportOutOfBandMessage("EventSourceException: "+Environment.GetResourceString("EventSource_EventTooBig"), true);
1373 throw new EventSourceException(Environment.GetResourceString("EventSource_EventTooBig"));
1374 case EventProvider.WriteEventErrorCode.NoFreeBuffers:
1375 ReportOutOfBandMessage("EventSourceException: "+Environment.GetResourceString("EventSource_NoFreeBuffers"), true);
1376 throw new EventSourceException(Environment.GetResourceString("EventSource_NoFreeBuffers"));
1377 case EventProvider.WriteEventErrorCode.NullInput:
1378 ReportOutOfBandMessage("EventSourceException: "+Environment.GetResourceString("EventSource_NullInput"), true);
1379 throw new EventSourceException(Environment.GetResourceString("EventSource_NullInput"));
1380 case EventProvider.WriteEventErrorCode.TooManyArgs:
1381 ReportOutOfBandMessage("EventSourceException: "+Environment.GetResourceString("EventSource_TooManyArgs"), true);
1382 throw new EventSourceException(Environment.GetResourceString("EventSource_TooManyArgs"));
1383 default:
1384 ReportOutOfBandMessage("EventSourceException", true);
1385 throw new EventSourceException();
1390 private void ValidateEventOpcodeForTransfer(ref EventMetadata eventData)
1392 if ((EventOpcode)eventData.Descriptor.Opcode != EventOpcode.Send &&
1393 (EventOpcode)eventData.Descriptor.Opcode != EventOpcode.Receive)
1395 ThrowEventSourceException();
1399 #if FEATURE_MANAGED_ETW
1400 /// <summary>
1401 /// This class lets us hook the 'OnEventCommand' from the eventSource.
1402 /// </summary>
1403 private class OverideEventProvider : EventProvider
1405 public OverideEventProvider(EventSource eventSource)
1407 this.m_eventSource = eventSource;
1409 protected override void OnControllerCommand(ControllerCommand command, IDictionary<string, string> arguments,
1410 int perEventSourceSessionId, int etwSessionId)
1412 // We use null to represent the ETW EventListener.
1413 EventListener listener = null;
1414 m_eventSource.SendCommand(listener, perEventSourceSessionId, etwSessionId,
1415 (EventCommand)command, IsEnabled(), Level, MatchAnyKeyword, arguments);
1417 private EventSource m_eventSource;
1419 #endif
1421 /// <summary>
1422 /// Used to hold all the static information about an event. This includes everything in the event
1423 /// descriptor as well as some stuff we added specifically for EventSource. see the
1424 /// code:m_eventData for where we use this.
1425 /// </summary>
1426 internal struct EventMetadata
1428 public System.Diagnostics.Tracing.EventDescriptor Descriptor;
1429 public bool EnabledForAnyListener; // true if any dispatcher has this event turned on
1430 public bool EnabledForETW; // is this event on for the OS ETW data dispatcher?
1431 public byte TriggersActivityTracking; // count of listeners that marked this event as trigger for start of activity logging.
1432 public string Name; // the name of the event
1433 public string Message; // If the event has a message associated with it, this is it.
1434 public ParameterInfo[] Parameters; //
1437 // This is the internal entry point that code:EventListeners call when wanting to send a command to a
1438 // eventSource. The logic is as follows
1440 // * if Command == Update
1441 // * perEventSourceSessionId specifies the per-provider ETW session ID that the command applies
1442 // to (if listener != null)
1443 // perEventSourceSessionId = 0 - reserved for EventListeners
1444 // perEventSourceSessionId = 1..SessionMask.MAX - reserved for activity tracing aware ETW sessions
1445 // perEventSourceSessionId-1 represents the bit in the reserved field (bits 44..47) in
1446 // Keywords that identifies the session
1447 // perEventSourceSessionId = SessionMask.MAX+1 - reserved for legacy ETW sessions; these are
1448 // discriminated by etwSessionId
1449 // * etwSessionId specifies a machine-wide ETW session ID; this allows correlation of
1450 // activity tracing across different providers (which might have different sessionIds
1451 // for the same ETW session)
1452 // * enable, level, matchAnyKeywords are used to set a default for all events for the
1453 // eventSource. In particular, if 'enabled' is false, 'level' and
1454 // 'matchAnyKeywords' are not used.
1455 // * OnEventCommand is invoked, which may cause calls to
1456 // code:EventSource.EnableEventForDispatcher which may cause changes in the filtering
1457 // depending on the logic in that routine.
1458 // * else (command != Update)
1459 // * Simply call OnEventCommand. The expectation is that filtering is NOT changed.
1460 // * The 'enabled' 'level', matchAnyKeyword' arguments are ignored (must be true, 0, 0).
1462 // dispatcher == null has special meaning. It is the 'ETW' dispatcher.
1463 internal void SendCommand(EventListener listener, int perEventSourceSessionId, int etwSessionId,
1464 EventCommand command, bool enable,
1465 EventLevel level, EventKeywords matchAnyKeyword,
1466 IDictionary<string, string> commandArguments)
1468 m_lastCommandException = null;
1469 bool shouldReport = (perEventSourceSessionId > 0) && (perEventSourceSessionId <= SessionMask.MAX);
1473 lock (EventListener.EventListenersLock)
1475 EnsureInitialized();
1477 // Find the per-EventSource dispatcher cooresponding to registered dispatcher
1478 EventDispatcher eventSourceDispatcher = GetDispatcher(listener);
1479 if (eventSourceDispatcher == null && listener != null) // dispatcher == null means ETW dispatcher
1480 throw new ArgumentException(Environment.GetResourceString("EventSource_ListenerNotFound"));
1482 if (commandArguments == null)
1483 commandArguments = new Dictionary<string, string>();
1485 if (command == EventCommand.Update)
1487 // Set it up using the 'standard' filtering bitfields (use the "global" enable, not session specific one)
1488 for (int i = 0; i < m_eventData.Length; i++)
1489 EnableEventForDispatcher(eventSourceDispatcher, i, IsEnabledByDefault(i, enable, level, matchAnyKeyword));
1491 if (enable)
1493 if (!m_eventSourceEnabled)
1495 // EventSource turned on for the first time, simply copy the bits.
1496 m_level = level;
1497 m_matchAnyKeyword = matchAnyKeyword;
1499 else
1501 // Already enabled, make it the most verbose of the existing and new filter
1502 if (level > m_level)
1503 m_level = level;
1504 if (matchAnyKeyword == 0)
1505 m_matchAnyKeyword = 0;
1506 else if (m_matchAnyKeyword != 0)
1507 m_matchAnyKeyword |= matchAnyKeyword;
1511 // interpret perEventSourceSessionId's sign, and adjust perEventSourceSessionId to
1512 // represent 0-based positive values
1513 bool bSessionEnable = (perEventSourceSessionId >= 0);
1514 if (perEventSourceSessionId == 0 && enable == false)
1515 bSessionEnable = false;
1517 if (listener == null)
1519 if (!bSessionEnable)
1520 perEventSourceSessionId = -perEventSourceSessionId;
1521 // for "global" enable/disable (passed in with listener == null and
1522 // perEventSourceSessionId == 0) perEventSourceSessionId becomes -1
1523 --perEventSourceSessionId;
1526 command = bSessionEnable ? EventCommand.Enable : EventCommand.Disable;
1528 // perEventSourceSessionId = -1 when ETW sent a notification, but the set of active sessions
1529 // hasn't changed.
1530 // sesisonId = SessionMask.MAX when one of the legacy ETW sessions changed
1531 // 0 <= perEventSourceSessionId < SessionMask.MAX for activity-tracing aware sessions
1532 Contract.Assert(perEventSourceSessionId >= -1 && perEventSourceSessionId <= SessionMask.MAX);
1534 // Send the manifest if we are enabling an ETW session
1535 if (bSessionEnable && eventSourceDispatcher == null)
1537 // eventSourceDispatcher == null means this is the ETW manifest
1539 // SendCommand can be called from the EventSource constructor as a side effect of
1540 // ETW registration. Unfortunately when this callback is active the provider is
1541 // not actually enabled (WriteEvents will fail). Thus if we detect this condition
1542 // (that we are still being constructed), we simply skip sending the manifest.
1543 // When the constructor completes we will try again and send the manifest at that time.
1545 // Note that we unconditionally send the manifest whenever we are enabled, even if
1546 // we were already enabled. This is because there may be multiple sessions active
1547 // and we can't know that all the sessions have seen the manifest.
1548 if (m_completelyInited)
1549 SendManifest(m_rawManifest);
1550 else
1551 m_deferedSendManifest = true;
1554 #if FEATURE_ACTIVITYSAMPLING
1555 if (bSessionEnable && perEventSourceSessionId != -1)
1557 bool participateInSampling = false;
1558 string activityFilters;
1559 int sessionIdBit;
1561 ParseCommandArgs(commandArguments, out participateInSampling,
1562 out activityFilters, out sessionIdBit);
1564 if (listener == null && commandArguments.Count > 0 && perEventSourceSessionId != sessionIdBit)
1566 throw new ArgumentException(Environment.GetResourceString("EventSource_SessionIdError",
1567 perEventSourceSessionId+SessionMask.SHIFT_SESSION_TO_KEYWORD,
1568 sessionIdBit+SessionMask.SHIFT_SESSION_TO_KEYWORD));
1571 if (listener == null)
1573 UpdateEtwSession(perEventSourceSessionId, etwSessionId, true, activityFilters, participateInSampling);
1575 else
1577 ActivityFilter.UpdateFilter(ref listener.m_activityFilter, this, 0, activityFilters);
1578 eventSourceDispatcher.m_activityFilteringEnabled = participateInSampling;
1581 else if (!bSessionEnable && listener == null)
1583 // if we disable an ETW session, indicate that in a synthesized command argument
1584 if (perEventSourceSessionId >= 0 && perEventSourceSessionId < SessionMask.MAX)
1586 commandArguments["EtwSessionKeyword"] = (perEventSourceSessionId+SessionMask.SHIFT_SESSION_TO_KEYWORD).ToString(CultureInfo.InvariantCulture);
1589 #endif // FEATURE_ACTIVITYSAMPLING
1591 this.OnEventCommand(new EventCommandEventArgs(command, commandArguments, this, eventSourceDispatcher));
1593 #if FEATURE_ACTIVITYSAMPLING
1594 if (listener == null && !bSessionEnable && perEventSourceSessionId != -1)
1596 // if we disable an ETW session, complete disabling it
1597 UpdateEtwSession(perEventSourceSessionId, etwSessionId, false, null, false);
1599 #endif // FEATURE_ACTIVITYSAMPLING
1601 if (enable)
1603 m_eventSourceEnabled = true;
1605 else
1607 // If we are disabling, maybe we can turn on 'quick checks' to filter
1608 // quickly. These are all just optimizations (since later checks will still filter)
1610 #if FEATURE_ACTIVITYSAMPLING
1611 // Turn off (and forget) any information about Activity Tracing.
1612 if (listener == null)
1614 // reset all filtering information for activity-tracing-aware sessions
1615 for (int i = 0; i < SessionMask.MAX; ++i)
1617 EtwSession etwSession = m_etwSessionIdMap[i];
1618 if (etwSession != null)
1619 ActivityFilter.DisableFilter(ref etwSession.m_activityFilter, this);
1621 m_activityFilteringForETWEnabled = new SessionMask(0);
1622 m_curLiveSessions = new SessionMask(0);
1623 // reset activity-tracing-aware sessions
1624 if (m_etwSessionIdMap != null)
1625 for (int i = 0; i < SessionMask.MAX; ++i)
1626 m_etwSessionIdMap[i] = null;
1627 // reset legacy sessions
1628 if (m_legacySessions != null)
1629 m_legacySessions.Clear();
1631 else
1633 ActivityFilter.DisableFilter(ref listener.m_activityFilter, this);
1634 eventSourceDispatcher.m_activityFilteringEnabled = false;
1636 #endif // FEATURE_ACTIVITYSAMPLING
1638 // There is a good chance EnabledForAnyListener are not as accurate as
1639 // they could be, go ahead and get a better estimate.
1640 for (int i = 0; i < m_eventData.Length; i++)
1642 bool isEnabledForAnyListener = false;
1643 for (EventDispatcher dispatcher = m_Dispatchers; dispatcher != null; dispatcher = dispatcher.m_Next)
1645 if (dispatcher.m_EventEnabled[i])
1647 isEnabledForAnyListener = true;
1648 break;
1651 m_eventData[i].EnabledForAnyListener = isEnabledForAnyListener;
1654 // If no events are enabled, disable the global enabled bit.
1655 if (!AnyEventEnabled())
1657 m_level = 0;
1658 m_matchAnyKeyword = 0;
1659 m_eventSourceEnabled = false;
1662 #if FEATURE_ACTIVITYSAMPLING
1663 UpdateKwdTriggers(enable);
1664 #endif // FEATURE_ACTIVITYSAMPLING
1666 else
1668 if (command == EventCommand.SendManifest)
1669 SendManifest(m_rawManifest);
1671 // These are not used for non-update commands and thus should always be 'default' values
1672 Contract.Assert(enable == true);
1673 Contract.Assert(m_level == EventLevel.LogAlways);
1674 Contract.Assert(m_matchAnyKeyword == EventKeywords.None);
1676 this.OnEventCommand(new EventCommandEventArgs(command, commandArguments, null, null));
1679 #if FEATURE_ACTIVITYSAMPLING
1680 if (m_completelyInited && (listener != null || shouldReport))
1682 SessionMask m = SessionMask.FromId(perEventSourceSessionId);
1683 ReportActivitySamplingInfo(listener, m);
1685 OutputDebugString(string.Format(CultureInfo.InvariantCulture, "{0}.SendCommand(session {1}, cmd {2}, enable {3}, level {4}): live sessions {5:x}, sampling {6:x}",
1686 m_name, perEventSourceSessionId, command, enable, level,
1687 (ulong) m_curLiveSessions, (ulong) m_activityFilteringForETWEnabled));
1688 #endif // FEATURE_ACTIVITYSAMPLING
1691 catch (Exception e)
1693 // Remember any exception and rethrow.
1694 m_lastCommandException = e;
1695 throw;
1699 #if FEATURE_ACTIVITYSAMPLING
1701 internal void UpdateEtwSession(
1702 int sessionIdBit,
1703 int etwSessionId,
1704 bool bEnable,
1705 string activityFilters,
1706 bool participateInSampling)
1708 if (sessionIdBit < SessionMask.MAX)
1710 // activity-tracing-aware etw session
1711 if (bEnable)
1713 var etwSession = EtwSession.GetEtwSession(etwSessionId, true);
1714 ActivityFilter.UpdateFilter(ref etwSession.m_activityFilter, this, sessionIdBit, activityFilters);
1715 m_etwSessionIdMap[sessionIdBit] = etwSession;
1716 m_activityFilteringForETWEnabled[sessionIdBit] = participateInSampling;
1718 else
1720 var etwSession = EtwSession.GetEtwSession(etwSessionId);
1721 ActivityFilter.DisableFilter(ref etwSession.m_activityFilter, this);
1722 m_etwSessionIdMap[sessionIdBit] = null;
1723 m_activityFilteringForETWEnabled[sessionIdBit] = false;
1724 // the ETW session is going away; remove it from the global list
1725 EtwSession.RemoveEtwSession(etwSession);
1727 m_curLiveSessions[sessionIdBit] = bEnable;
1729 else
1731 // legacy etw session
1732 if (bEnable)
1734 if (m_legacySessions == null)
1735 m_legacySessions = new List<EtwSession>(8);
1736 var etwSession = EtwSession.GetEtwSession(etwSessionId, true);
1737 if (!m_legacySessions.Contains(etwSession))
1738 m_legacySessions.Add(etwSession);
1740 else
1742 var etwSession = EtwSession.GetEtwSession(etwSessionId);
1743 if (m_legacySessions != null)
1744 m_legacySessions.Remove(etwSession);
1745 // the ETW session is going away; remove it from the global list
1746 EtwSession.RemoveEtwSession(etwSession);
1751 internal static bool ParseCommandArgs(
1752 IDictionary<string, string> commandArguments,
1753 out bool participateInSampling,
1754 out string activityFilters,
1755 out int sessionIdBit)
1757 bool res = true;
1758 participateInSampling = false;
1759 string activityFilterString;
1760 if (commandArguments.TryGetValue("ActivitySamplingStartEvent", out activityFilters))
1762 // if a start event is specified default the event source to participate in sampling
1763 participateInSampling = true;
1766 if (commandArguments.TryGetValue("ActivitySampling", out activityFilterString))
1768 if (string.Compare(activityFilterString, "false", StringComparison.OrdinalIgnoreCase) == 0 ||
1769 activityFilterString == "0")
1770 participateInSampling = false;
1771 else
1772 participateInSampling = true;
1775 string sSessionKwd;
1776 int sessionKwd = -1;
1777 if (!commandArguments.TryGetValue("EtwSessionKeyword", out sSessionKwd) ||
1778 !int.TryParse(sSessionKwd, out sessionKwd) ||
1779 sessionKwd < SessionMask.SHIFT_SESSION_TO_KEYWORD ||
1780 sessionKwd >= SessionMask.SHIFT_SESSION_TO_KEYWORD + SessionMask.MAX)
1782 sessionIdBit = -1;
1783 res = false;
1785 else
1787 sessionIdBit = sessionKwd - SessionMask.SHIFT_SESSION_TO_KEYWORD;
1789 return res;
1792 internal void UpdateKwdTriggers(bool enable)
1794 if (enable)
1796 // recompute m_keywordTriggers
1797 ulong gKeywords = (ulong)m_matchAnyKeyword;
1798 if (gKeywords == 0)
1799 gKeywords = 0xFFFFffffFFFFffff;
1801 m_keywordTriggers = 0;
1802 for (int sessId = 0; sessId < SessionMask.MAX; ++sessId)
1804 EtwSession etwSession = m_etwSessionIdMap[sessId];
1805 if (etwSession == null)
1806 continue;
1808 ActivityFilter activityFilter = etwSession.m_activityFilter;
1809 ActivityFilter.UpdateKwdTriggers(activityFilter, m_guid, this, (EventKeywords)gKeywords);
1812 else
1814 m_keywordTriggers = 0;
1818 #endif // FEATURE_ACTIVITYSAMPLING
1820 /// <summary>
1821 /// If 'value is 'true' then set the eventSource so that 'dispatcher' will recieve event with the eventId
1822 /// of 'eventId. If value is 'false' disable the event for that dispatcher. If 'eventId' is out of
1823 /// range return false, otherwise true.
1824 /// </summary>
1825 internal bool EnableEventForDispatcher(EventDispatcher dispatcher, int eventId, bool value)
1827 if (dispatcher == null)
1829 if (eventId >= m_eventData.Length)
1830 return false;
1831 #if FEATURE_MANAGED_ETW
1832 if (m_provider != null)
1833 m_eventData[eventId].EnabledForETW = value;
1834 #endif
1836 else
1838 if (eventId >= dispatcher.m_EventEnabled.Length)
1839 return false;
1840 dispatcher.m_EventEnabled[eventId] = value;
1841 if (value)
1842 m_eventData[eventId].EnabledForAnyListener = true;
1844 return true;
1847 /// <summary>
1848 /// Returns true if any event at all is on.
1849 /// </summary>
1850 private bool AnyEventEnabled()
1852 for (int i = 0; i < m_eventData.Length; i++)
1853 if (m_eventData[i].EnabledForETW || m_eventData[i].EnabledForAnyListener)
1854 return true;
1855 return false;
1858 [SecuritySafeCritical]
1859 private void EnsureInitialized()
1861 Contract.Assert(Monitor.IsEntered(EventListener.EventListenersLock));
1862 if (m_rawManifest == null)
1864 Contract.Assert(m_rawManifest == null);
1865 m_rawManifest = CreateManifestAndDescriptors(this.GetType(), Name, this);
1868 foreach (WeakReference eventSourceRef in EventListener.s_EventSources)
1870 EventSource eventSource = eventSourceRef.Target as EventSource;
1871 if (eventSource != null && eventSource.Guid == m_guid)
1873 if (eventSource != this)
1874 throw new ArgumentException(Environment.GetResourceString("EventSource_EventSourceGuidInUse", m_guid));
1878 // Make certain all dispatchers are also have their array's initialized
1879 EventDispatcher dispatcher = m_Dispatchers;
1880 while (dispatcher != null)
1882 if (dispatcher.m_EventEnabled == null)
1883 dispatcher.m_EventEnabled = new bool[m_eventData.Length];
1884 dispatcher = dispatcher.m_Next;
1887 if (s_currentPid == 0)
1889 s_currentPid = Win32Native.GetCurrentProcessId();
1893 // Send out the ETW manifest XML out to ETW
1894 // Today, we only send the manifest to ETW, custom listeners don't get it.
1895 [SecuritySafeCritical]
1896 private unsafe bool SendManifest(byte[] rawManifest)
1898 bool success = true;
1900 #if FEATURE_MANAGED_ETW
1901 fixed (byte* dataPtr = rawManifest)
1903 var manifestDescr = new System.Diagnostics.Tracing.EventDescriptor(0xFFFE, 1, 0, 0, 0xFE, 0xFFFE, -1);
1904 ManifestEnvelope envelope = new ManifestEnvelope();
1906 envelope.Format = ManifestEnvelope.ManifestFormats.SimpleXmlFormat;
1907 envelope.MajorVersion = 1;
1908 envelope.MinorVersion = 0;
1909 envelope.Magic = 0x5B; // An unusual number that can be checked for consistancy.
1910 int dataLeft = rawManifest.Length;
1911 envelope.TotalChunks = (ushort)((dataLeft + (ManifestEnvelope.MaxChunkSize - 1)) / ManifestEnvelope.MaxChunkSize);
1912 envelope.ChunkNumber = 0;
1914 EventProvider.EventData* dataDescrs = stackalloc EventProvider.EventData[2];
1915 dataDescrs[0].Ptr = (ulong)&envelope;
1916 dataDescrs[0].Size = (uint)sizeof(ManifestEnvelope);
1917 dataDescrs[0].Reserved = 0;
1919 dataDescrs[1].Ptr = (ulong)dataPtr;
1920 dataDescrs[1].Reserved = 0;
1922 int chunkSize = ManifestEnvelope.MaxChunkSize;
1923 TRY_AGAIN_WITH_SMALLER_CHUNK_SIZE:
1925 while (dataLeft > 0)
1927 dataDescrs[1].Size = (uint)Math.Min(dataLeft, chunkSize);
1928 if (m_provider != null)
1930 if (!m_provider.WriteEvent(ref manifestDescr, null, 2, (IntPtr)dataDescrs))
1932 // Turns out that if users set the BufferSize to something less than 64K then WriteEvent
1933 // can fail. If we get this failure on the first chunk try again with something smaller
1934 // The smallest BufferSize is 1K so if we get to 512, we can give up making it smaller.
1935 if (EventProvider.GetLastWriteEventError() == EventProvider.WriteEventErrorCode.EventTooBig)
1937 chunkSize = chunkSize / 2;
1938 if (envelope.ChunkNumber == 0 && chunkSize > 512)
1939 goto TRY_AGAIN_WITH_SMALLER_CHUNK_SIZE;
1941 success = false;
1942 if(m_throwOnEventWriteErrors)
1943 ThrowEventSourceException();
1946 dataLeft -= ManifestEnvelope.MaxChunkSize;
1947 dataDescrs[1].Ptr += ManifestEnvelope.MaxChunkSize;
1948 envelope.ChunkNumber++;
1951 #endif
1953 return success;
1957 // Helper to deal with the fact that the type we are reflecting over might be loaded in the ReflectionOnly context.
1958 // When that is the case, we have the build the custom assemblies on a member by hand.
1959 internal static Attribute GetCustomAttributeHelper(MemberInfo member, Type attributeType)
1961 if (!member.Module.Assembly.ReflectionOnly)
1963 // Let the runtime to the work for us, since we can execute code in this context.
1964 return Attribute.GetCustomAttribute(member, attributeType, false);
1967 // In the reflection only context, we have to do things by hand.
1968 string fullTypeNameToFind = attributeType.FullName;
1970 #if EVENT_SOURCE_LEGACY_NAMESPACE_SUPPORT
1971 fullTypeNameToFind = fullTypeNameToFind.Replace("System.Diagnostics.Eventing", "System.Diagnostics.Tracing");
1972 #endif
1974 foreach (CustomAttributeData data in CustomAttributeData.GetCustomAttributes(member))
1976 string attributeFullTypeName = data.Constructor.ReflectedType.FullName;
1977 #if EVENT_SOURCE_LEGACY_NAMESPACE_SUPPORT
1978 attributeFullTypeName = attributeFullTypeName.Replace("System.Diagnostics.Eventing", "System.Diagnostics.Tracing");
1979 #endif
1981 if (String.Equals(attributeFullTypeName, fullTypeNameToFind, StringComparison.Ordinal))
1983 Attribute attr = null;
1985 Contract.Assert(data.ConstructorArguments.Count <= 1);
1987 if (data.ConstructorArguments.Count == 1)
1989 attr = (Attribute)Activator.CreateInstance(attributeType, new object[] { data.ConstructorArguments[0].Value });
1991 else if (data.ConstructorArguments.Count == 0)
1993 attr = (Attribute)Activator.CreateInstance(attributeType);
1996 if (attr != null)
1998 Type t = attr.GetType();
2000 foreach (CustomAttributeNamedArgument namedArgument in data.NamedArguments)
2002 PropertyInfo p = t.GetProperty(namedArgument.MemberInfo.Name, BindingFlags.Public | BindingFlags.Instance);
2003 object value = namedArgument.TypedValue.Value;
2005 if (p.PropertyType.IsEnum)
2007 value = Enum.Parse(p.PropertyType, value.ToString());
2010 p.SetValue(attr, value, null);
2013 return attr;
2018 return null;
2021 // Use reflection to look at the attributes of a class, and generate a manifest for it (as UTF8) and
2022 // return the UTF8 bytes. It also sets up the code:EventData structures needed to dispatch events
2023 // at run time. 'source' is the event source to place the descriptors. If it is null,
2024 // then the descriptors are not creaed, and just the manifest is generated.
2025 private static byte[] CreateManifestAndDescriptors(Type eventSourceType, string eventSourceDllName, EventSource source)
2027 MethodInfo[] methods = eventSourceType.GetMethods(BindingFlags.DeclaredOnly | BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance);
2028 EventAttribute defaultEventAttribute;
2029 int eventId = 1; // The number given to an event that does not have a explicitly given ID.
2030 EventMetadata[] eventData = null;
2031 Dictionary<string, string> eventsByName = null;
2032 if (source != null)
2033 eventData = new EventMetadata[methods.Length];
2035 // See if we have localization information.
2036 ResourceManager resources = null;
2037 EventSourceAttribute eventSourceAttrib = (EventSourceAttribute)GetCustomAttributeHelper(eventSourceType, typeof(EventSourceAttribute));
2038 if (eventSourceAttrib != null && eventSourceAttrib.LocalizationResources != null)
2039 resources = new ResourceManager(eventSourceAttrib.LocalizationResources, eventSourceType.Assembly);
2041 ManifestBuilder manifest = new ManifestBuilder(GetName(eventSourceType), GetGuid(eventSourceType), eventSourceDllName, resources);
2043 // Collect task, opcode, keyword and channel information
2044 #if FEATURE_MANAGED_ETW_CHANNELS
2045 foreach (var providerEnumKind in new string[] { "Keywords", "Tasks", "Opcodes", "Channels" })
2046 #else
2047 foreach (var providerEnumKind in new string[] { "Keywords", "Tasks", "Opcodes" })
2048 #endif
2050 Type nestedType = eventSourceType.GetNestedType(providerEnumKind);
2051 if (nestedType != null)
2053 foreach (FieldInfo staticField in nestedType.GetFields(BindingFlags.DeclaredOnly | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static))
2055 AddProviderEnumKind(manifest, staticField, providerEnumKind);
2060 for (int i = 0; i < methods.Length; i++)
2062 MethodInfo method = methods[i];
2063 ParameterInfo[] args = method.GetParameters();
2065 // Get the EventDescriptor (from the Custom attributes)
2066 EventAttribute eventAttribute = (EventAttribute)GetCustomAttributeHelper(method, typeof(EventAttribute));
2068 // Methods that don't return void can't be events.
2069 if (method.ReturnType != typeof(void))
2071 if (eventAttribute != null)
2072 throw new ArgumentException(Environment.GetResourceString("EventSource_AttributeOnNonVoid", method.Name));
2073 continue;
2075 if (method.IsVirtual || method.IsStatic)
2077 continue;
2080 if (eventAttribute == null)
2082 // If we explictly mark the method as not being an event, then honor that.
2083 if (GetCustomAttributeHelper(method, typeof(NonEventAttribute)) != null)
2084 continue;
2086 defaultEventAttribute = new EventAttribute(eventId);
2087 eventAttribute = defaultEventAttribute;
2089 else if (eventAttribute.EventId <= 0)
2090 throw new ArgumentException(Environment.GetResourceString("EventSource_NeedPositiveId"));
2091 else if ((ulong)eventAttribute.Keywords >= 0x0000100000000000UL)
2092 throw new ArgumentException(Environment.GetResourceString("EventSource_ReservedKeywords"));
2093 eventId++;
2095 // Auto-assign tasks, starting with the highest task number and working back
2096 if (eventAttribute.Opcode == EventOpcode.Info && eventAttribute.Task == EventTask.None)
2097 eventAttribute.Task = (EventTask)(0xFFFE - eventAttribute.EventId);
2099 manifest.StartEvent(method.Name, eventAttribute);
2100 for (int fieldIdx = 0; fieldIdx < args.Length; fieldIdx++)
2102 // If the first parameter is 'RelatedActivityId' then skip it.
2103 if (fieldIdx == 0 && args[fieldIdx].ParameterType == typeof(Guid) &&
2104 string.Compare(args[fieldIdx].Name, "RelatedActivityId", StringComparison.OrdinalIgnoreCase) == 0)
2105 continue;
2106 manifest.AddEventParameter(args[fieldIdx].ParameterType, args[fieldIdx].Name);
2108 manifest.EndEvent();
2110 if (source != null)
2112 // Do checking for user errors (optional, but nto a big deal so we do it).
2113 DebugCheckEvent(ref eventsByName, eventData, method, eventAttribute);
2114 AddEventDescriptor(ref eventData, method.Name, eventAttribute, args);
2118 if (source != null)
2120 TrimEventDescriptors(ref eventData);
2121 source.m_eventData = eventData; // officaly initialize it. We do this at most once (it is racy otherwise).
2124 return manifest.CreateManifest();
2127 // adds a enumeration (keyword, opcode, task or channel) represented by 'staticField'
2128 // to the manifest.
2129 private static void AddProviderEnumKind(ManifestBuilder manifest, FieldInfo staticField, string providerEnumKind)
2131 Type staticFieldType = staticField.FieldType;
2132 if (staticFieldType == typeof(EventOpcode))
2134 if (providerEnumKind != "Opcodes") goto Error;
2135 int value = (int)staticField.GetRawConstantValue();
2136 if (value <= 10)
2137 throw new ArgumentException(Environment.GetResourceString("EventSource_ReservedOpcode"));
2138 manifest.AddOpcode(staticField.Name, value);
2140 else if (staticFieldType == typeof(EventTask))
2142 if (providerEnumKind != "Tasks") goto Error;
2143 manifest.AddTask(staticField.Name, (int)staticField.GetRawConstantValue());
2145 else if (staticFieldType == typeof(EventKeywords))
2147 if (providerEnumKind != "Keywords") goto Error;
2148 manifest.AddKeyword(staticField.Name, (ulong)(long)staticField.GetRawConstantValue());
2150 #if FEATURE_MANAGED_ETW_CHANNELS
2151 else if (staticFieldType == typeof(EventChannel))
2153 if (providerEnumKind != "Channels") goto Error;
2154 var channelAttribute = (ChannelAttribute)GetCustomAttributeHelper(staticField, typeof(ChannelAttribute));
2155 manifest.AddChannel(staticField.Name, (byte)staticField.GetRawConstantValue(), channelAttribute);
2157 #endif
2158 return;
2159 Error:
2160 throw new ArgumentException(Environment.GetResourceString("EventSource_EnumKindMismatch", staticField.FieldType.Name, providerEnumKind));
2163 // Helper used by code:CreateManifestAndDescriptors to add a code:EventData descriptor for a method
2164 // with the code:EventAttribute 'eventAttribute'. resourceManger may be null in which case we populate it
2165 // it is populated if we need to look up message resources
2166 private static void AddEventDescriptor(ref EventMetadata[] eventData, string eventName,
2167 EventAttribute eventAttribute, ParameterInfo[] eventParameters)
2169 if (eventData == null || eventData.Length <= eventAttribute.EventId)
2171 EventMetadata[] newValues = new EventMetadata[Math.Max(eventData.Length + 16, eventAttribute.EventId + 1)];
2172 Array.Copy(eventData, newValues, eventData.Length);
2173 eventData = newValues;
2176 eventData[eventAttribute.EventId].Descriptor = new System.Diagnostics.Tracing.EventDescriptor(
2177 eventAttribute.EventId,
2178 eventAttribute.Version,
2179 #if FEATURE_MANAGED_ETW_CHANNELS
2180 (byte)eventAttribute.Channel,
2181 #else
2182 (byte)0,
2183 #endif
2184 (byte)eventAttribute.Level,
2185 (byte)eventAttribute.Opcode,
2186 (int)eventAttribute.Task,
2187 (long)((ulong)eventAttribute.Keywords | SessionMask.All.ToEventKeywords()));
2189 eventData[eventAttribute.EventId].Name = eventName;
2190 eventData[eventAttribute.EventId].Parameters = eventParameters;
2191 eventData[eventAttribute.EventId].Message = eventAttribute.Message;
2194 // Helper used by code:CreateManifestAndDescriptors that trims the m_eventData array to the correct
2195 // size after all event descriptors have been added.
2196 private static void TrimEventDescriptors(ref EventMetadata[] eventData)
2198 int idx = eventData.Length;
2199 while (0 < idx)
2201 --idx;
2202 if (eventData[idx].Descriptor.EventId != 0)
2203 break;
2205 if (eventData.Length - idx > 2) // allow one wasted slot.
2207 EventMetadata[] newValues = new EventMetadata[idx + 1];
2208 Array.Copy(eventData, newValues, newValues.Length);
2209 eventData = newValues;
2213 // Helper used by code:EventListener.AddEventSource and code:EventListener.EventListener
2214 // when a listener gets attached to a eventSource
2215 internal void AddListener(EventListener listener)
2217 lock (EventListener.EventListenersLock)
2219 bool[] enabledArray = null;
2220 if (m_eventData != null)
2221 enabledArray = new bool[m_eventData.Length];
2222 m_Dispatchers = new EventDispatcher(m_Dispatchers, enabledArray, listener);
2223 listener.OnEventSourceCreated(this);
2227 // Helper used by code:CreateManifestAndDescriptors to find user mistakes like reusing an event
2228 // index for two distinct events etc. Throws exceptions when it finds something wrong.
2229 private static void DebugCheckEvent(ref Dictionary<string, string> eventsByName,
2230 EventMetadata[] eventData, MethodInfo method, EventAttribute eventAttribute)
2232 int eventArg = GetHelperCallFirstArg(method);
2233 if (eventArg >= 0 && eventAttribute.EventId != eventArg)
2235 throw new ArgumentException(Environment.GetResourceString("EventSource_MismatchIdToWriteEvent", method.Name, eventAttribute.EventId, eventArg));
2238 if (eventAttribute.EventId < eventData.Length && eventData[eventAttribute.EventId].Descriptor.EventId != 0)
2240 throw new ArgumentException(Environment.GetResourceString("EventSource_EventIdReused", method.Name, eventAttribute.EventId));
2243 if (eventsByName == null)
2244 eventsByName = new Dictionary<string, string>();
2246 if (eventsByName.ContainsKey(method.Name))
2247 throw new ArgumentException(Environment.GetResourceString("EventSource_EventNameReused", method.Name));
2249 eventsByName[method.Name] = method.Name;
2252 /// <summary>
2253 /// This method looks at the IL and tries to pattern match against the standard
2254 /// 'boilerplate' event body
2255 ///
2256 /// { if (Enabled()) WriteEvent(#, ...) }
2257 ///
2258 /// If the pattern matches, it returns the literal number passed as the first parameter to
2259 /// the WriteEvent. This is used to find common user errors (mismatching this
2260 /// number with the EventAttribute ID). It is only used for validation.
2261 /// </summary>
2262 /// <param name="method">The method to probe.</param>
2263 /// <returns>The literal value or -1 if the value could not be determined. </returns>
2264 [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Maintainability", "CA1502:AvoidExcessiveComplexity", Justification = "Switch statement is clearer than alternatives")]
2265 [SecuritySafeCritical]
2266 static private int GetHelperCallFirstArg(MethodInfo method)
2268 // we need this permission in low trust
2269 new ReflectionPermission(ReflectionPermissionFlag.MemberAccess).Assert();
2270 // Currently searches for the following pattern
2272 // ... // CAN ONLY BE THE INSTRUCTIONS BELOW
2273 // LDARG0
2274 // LDC.I4 XXX
2275 // ... // CAN ONLY BE THE INSTRUCTIONS BELOW CAN'T BE A BRANCH OR A CALL
2276 // CALL
2277 // NOP // 0 or more times
2278 // RET
2280 // If we find this pattern we return the XXX. Otherwise we return -1.
2281 byte[] instrs = method.GetMethodBody().GetILAsByteArray();
2282 int retVal = -1;
2283 for (int idx = 0; idx < instrs.Length; )
2285 switch (instrs[idx])
2287 case 0: // NOP
2288 case 1: // BREAK
2289 case 2: // LDARG_0
2290 case 3: // LDARG_1
2291 case 4: // LDARG_2
2292 case 5: // LDARG_3
2293 case 6: // LDLOC_0
2294 case 7: // LDLOC_1
2295 case 8: // LDLOC_2
2296 case 9: // LDLOC_3
2297 case 10: // STLOC_0
2298 case 11: // STLOC_1
2299 case 12: // STLOC_2
2300 case 13: // STLOC_3
2301 break;
2302 case 14: // LDARG_S
2303 case 16: // STARG_S
2304 idx++;
2305 break;
2306 case 20: // LDNULL
2307 break;
2308 case 21: // LDC_I4_M1
2309 case 22: // LDC_I4_0
2310 case 23: // LDC_I4_1
2311 case 24: // LDC_I4_2
2312 case 25: // LDC_I4_3
2313 case 26: // LDC_I4_4
2314 case 27: // LDC_I4_5
2315 case 28: // LDC_I4_6
2316 case 29: // LDC_I4_7
2317 case 30: // LDC_I4_8
2318 if (idx > 0 && instrs[idx - 1] == 2) // preceeded by LDARG0
2319 retVal = instrs[idx] - 22;
2320 break;
2321 case 31: // LDC_I4_S
2322 if (idx > 0 && instrs[idx - 1] == 2) // preceeded by LDARG0
2323 retVal = instrs[idx + 1];
2324 idx++;
2325 break;
2326 case 32: // LDC_I4
2327 idx += 4;
2328 break;
2329 case 37: // DUP
2330 break;
2331 case 40: // CALL
2332 idx += 4;
2334 if (retVal >= 0)
2336 // Is this call just before return?
2337 for (int search = idx + 1; search < instrs.Length; search++)
2339 if (instrs[search] == 42) // RET
2340 return retVal;
2341 if (instrs[search] != 0) // NOP
2342 break;
2345 retVal = -1;
2346 break;
2347 case 44: // BRFALSE_S
2348 case 45: // BRTRUE_S
2349 retVal = -1;
2350 idx++;
2351 break;
2352 case 57: // BRFALSE
2353 case 58: // BRTRUE
2354 retVal = -1;
2355 idx += 4;
2356 break;
2357 case 103: // CONV_I1
2358 case 104: // CONV_I2
2359 case 105: // CONV_I4
2360 case 106: // CONV_I8
2361 case 109: // CONV_U4
2362 case 110: // CONV_U8
2363 break;
2364 case 140: // BOX
2365 case 141: // NEWARR
2366 idx += 4;
2367 break;
2368 case 162: // STELEM_REF
2369 break;
2370 case 254: // PREFIX
2371 idx++;
2372 // Covers the CEQ instructions used in debug code for some reason.
2373 if (idx >= instrs.Length || instrs[idx] >= 6)
2374 goto default;
2375 break;
2376 default:
2377 /* Contract.Assert(false, "Warning: User validation code sub-optimial: Unsuported opcode " + instrs[idx] +
2378 " at " + idx + " in method " + method.Name); */
2379 return -1;
2381 idx++;
2383 return -1;
2386 [Conditional("DEBUG")]
2387 internal static void OutputDebugString(string msg)
2389 msg = msg.TrimEnd('\r', '\n') +
2390 string.Format(CultureInfo.InvariantCulture, ", Thrd({0})"+Environment.NewLine, Thread.CurrentThread.ManagedThreadId);
2391 System.Diagnostics.Debugger.Log(0, null, msg);
2394 [SuppressMessage("Microsoft.Concurrency", "CA8001", Justification = "This does not need to be correct when racing with other threads")]
2395 internal void ReportOutOfBandMessage(string msg, bool flush)
2397 // msg == null is a signal to flush what's accumulated in the buffer
2398 if (msg == null && flush)
2400 if (!string.IsNullOrEmpty(m_deferredErrorInfo))
2402 WriteString(m_deferredErrorInfo);
2403 m_deferredErrorInfo = String.Empty;
2405 return;
2408 if (!msg.EndsWith(Environment.NewLine, StringComparison.Ordinal))
2409 msg = msg + Environment.NewLine;
2411 // send message to debugger without delay
2412 System.Diagnostics.Debugger.Log(0, null, msg);
2414 m_deferredErrorInfo = m_deferredErrorInfo + msg;
2415 if (flush)
2417 // send message to the ETW listener if available
2418 WriteString(m_deferredErrorInfo);
2419 m_deferredErrorInfo = String.Empty;
2423 #if FEATURE_ACTIVITYSAMPLING
2424 private void ReportActivitySamplingInfo(EventListener listener, SessionMask sessions)
2426 Contract.Assert(listener == null || (uint)sessions == (uint)SessionMask.FromId(0));
2428 for (int perEventSourceSessionId = 0; perEventSourceSessionId < SessionMask.MAX; ++perEventSourceSessionId)
2430 if (!sessions[perEventSourceSessionId])
2431 continue;
2433 ActivityFilter af;
2434 if (listener == null)
2436 EtwSession etwSession = m_etwSessionIdMap[perEventSourceSessionId];
2437 Contract.Assert(etwSession != null);
2438 af = etwSession.m_activityFilter;
2440 else
2442 af = listener.m_activityFilter;
2445 if (af == null)
2446 continue;
2448 SessionMask m = new SessionMask();
2449 m[perEventSourceSessionId] = true;
2451 foreach (var t in af.GetFilterAsTuple(m_guid))
2453 WriteStringToListener(listener, string.Format(CultureInfo.InvariantCulture, "Session {0}: {1} = {2}", perEventSourceSessionId, t.Item1, t.Item2), m);
2456 bool participateInSampling = (listener == null) ?
2457 m_activityFilteringForETWEnabled[perEventSourceSessionId] :
2458 GetDispatcher(listener).m_activityFilteringEnabled;
2459 WriteStringToListener(listener, string.Format(CultureInfo.InvariantCulture, "Session {0}: Activity Sampling support: {1}",
2460 perEventSourceSessionId, participateInSampling ? "enabled" : "disabled"), m);
2463 #endif // FEATURE_ACTIVITYSAMPLING
2465 // private instance state
2466 private string m_name; // My friendly name (privided in ctor)
2467 internal int m_id; // A small integer that is unique to this instance.
2468 private Guid m_guid; // GUID representing the ETW eventSource to the OS.
2469 internal volatile EventMetadata[] m_eventData; // None per-event data
2470 private volatile byte[] m_rawManifest; // Bytes to send out representing the event schema
2471 private readonly bool m_throwOnEventWriteErrors; // If a listener throws and error, should we catch it or not
2473 // Enabling bits
2474 private bool m_eventSourceEnabled; // am I enabled (any of my events are enabled for any dispatcher)
2475 internal EventLevel m_level; // higest level enabled by any output dispatcher
2476 internal EventKeywords m_matchAnyKeyword; // the logical OR of all levels enabled by any output dispatcher (zero is a special case) meaning 'all keywords'
2478 // Dispatching state
2479 internal volatile EventDispatcher m_Dispatchers; // Linked list of code:EventDispatchers we write the data to (we also do ETW specially)
2480 #if FEATURE_MANAGED_ETW
2481 private volatile OverideEventProvider m_provider; // This hooks up ETW commands to our 'OnEventCommand' callback
2482 #endif
2483 private bool m_completelyInited; // The EventSource constructor has returned without exception.
2484 private bool m_deferedSendManifest; // We did not send the manifest in the startup path
2485 private Exception m_lastCommandException; // If there was an exception during a command, this is it.
2486 private Exception m_constructionException; // If there was an exception construction, this is it
2487 private string m_deferredErrorInfo; // non-fatal error info accumulated during construction
2489 internal static uint s_currentPid; // current process id, used in synthesizing quasi-GUIDs
2491 #if FEATURE_ACTIVITYSAMPLING
2492 private SessionMask m_curLiveSessions; // the activity-tracing aware sessions' bits
2493 private EtwSession[] m_etwSessionIdMap; // the activity-tracing aware sessions
2494 private List<EtwSession> m_legacySessions; // the legacy ETW sessions listening to this source
2495 internal long m_keywordTriggers; // a bit is set if it corresponds to a keyword that's part of an enabled triggering event
2496 internal SessionMask m_activityFilteringForETWEnabled; // does THIS EventSource have activity filtering turned on for each ETW session
2497 static internal Action<Guid> s_activityDying; // Fires when something calls SetCurrentThreadToActivity()
2498 // Also used to mark that activity tracing is on for some case
2499 #endif // FEATURE_ACTIVITYSAMPLING
2500 #endregion
2503 /// <summary>
2504 /// An code:EventListener represents the target for all events generated by EventSources (that is
2505 /// subclasses of code:EventSource), in the currnet appdomain. When a new EventListener is created
2506 /// it is logically attached to all eventSources in that appdomain. When the EventListener is Disposed, then
2507 /// it is disconnected from the event eventSources. Note that there is a internal list of STRONG references
2508 /// to EventListeners, which means that relying on the lack of references ot EventListeners to clean up
2509 /// EventListeners will NOT work. You must call EventListener.Dispose explicitly when a dispatcher is no
2510 /// longer needed.
2511 ///
2512 /// Once created, EventListeners can enable or disable on a per-eventSource basis using verbosity levels
2513 /// (code:EventLevel) and bitfields code:EventKeywords to further restrict the set of events to be sent
2514 /// to the dispatcher. The dispatcher can also send arbitrary commands to a particular eventSource using the
2515 /// 'SendCommand' method. The meaning of the commands are eventSource specific.
2516 ///
2517 /// The Null Guid (that is (new Guid()) has special meaning as a wildcard for 'all current eventSources in
2518 /// the appdomain'. Thus it is relatively easy to turn on all events in the appdomain if desired.
2519 ///
2520 /// It is possible for there to be many EventListener's defined in a single appdomain. Each dispatcher is
2521 /// logically independent of the other listeners. Thus when one dispatcher enables or disables events, it
2522 /// affects only that dispatcher (other listeners get the events they asked for). It is possible that
2523 /// commands sent with 'SendCommand' would do a semantic operation that would affect the other listeners
2524 /// (like doing a GC, or flushing data ...), but this is the exception rather than the rule.
2525 ///
2526 /// Thus the model is that each EventSource keeps a list of EventListeners that it is sending events
2527 /// to. Associated with each EventSource-dispatcher pair is a set of filtering criteria that determine for
2528 /// that eventSource what events that dispatcher will recieve.
2529 ///
2530 /// Listeners receive the events on their 'OnEventWritten' method. Thus subclasses of EventListener must
2531 /// override this method to do something useful with the data.
2532 ///
2533 /// In addition, when new eventSources are created, the 'OnEventSourceCreate' method is called. The
2534 /// invariant associated with this callback is that every eventSource gets exactly one
2535 /// 'OnEventSourceCreate' call for ever eventSource that can potentially send it log messages. In
2536 /// particular when a EventListener is created, typically a series of OnEventSourceCreate' calls are
2537 /// made to notify the new dispatcher of all the eventSources that existed before the EventListener was
2538 /// created.
2539 ///
2540 /// </summary>
2541 public abstract class EventListener : IDisposable
2543 private static bool s_CreatingListener = false;
2544 /// <summary>
2545 /// Create a new EventListener in which all events start off truned off (use EnableEvents to turn
2546 /// them on).
2547 /// </summary>
2548 protected EventListener()
2550 lock (EventListenersLock)
2552 // Disallow creating EventListener reentrancy.
2553 if (s_CreatingListener)
2554 throw new InvalidOperationException(Environment.GetResourceString("EventSource_ListenerCreatedInsideCallback"));
2558 s_CreatingListener = true;
2560 // Add to list of listeners in the system, do this BEFORE firing the ‘OnEventSourceCreated’ so that
2561 // Those added sources see this listener.
2562 this.m_Next = s_Listeners;
2563 s_Listeners = this;
2565 // Find all existing eventSources call OnEventSourceCreated to 'catchup'
2566 // Note that we DO have reentrancy here because 'AddListener' calls out to user code (via OnEventSourceCreated callback)
2567 // We tolerate this by iterating over a copy of the list here. New event sources will take care of adding listeners themselves
2568 // EventSources are not guaranteed to be added at the end of the s_EventSource list -- We re-use slots when a new source
2569 // is created.
2570 WeakReference[] eventSourcesSnapshot = s_EventSources.ToArray();
2572 for (int i = 0; i < eventSourcesSnapshot.Length; i++)
2574 WeakReference eventSourceRef = eventSourcesSnapshot[i];
2575 EventSource eventSource = eventSourceRef.Target as EventSource;
2576 if (eventSource != null)
2577 eventSource.AddListener(this); // This will cause the OnEventSourceCreated callback to fire.
2580 Validate();
2582 finally
2584 s_CreatingListener = false;
2588 /// <summary>
2589 /// Dispose should be called when the EventListener no longer desires 'OnEvent*' callbacks. Because
2590 /// there is an internal list of strong references to all EventListeners, calling 'Displose' directly
2591 /// is the only way to actually make the listen die. Thus it is important that users of EventListener
2592 /// call Dispose when they are done with their logging.
2593 /// </summary>
2594 public virtual void Dispose()
2596 lock (EventListenersLock)
2598 Contract.Assert(s_Listeners != null);
2599 if (s_Listeners != null)
2601 if (this == s_Listeners)
2603 EventListener cur = s_Listeners;
2604 s_Listeners = this.m_Next;
2605 RemoveReferencesToListenerInEventSources(cur);
2607 else
2609 // Find 'this' from the s_Listeners linked list.
2610 EventListener prev = s_Listeners;
2611 for (; ; )
2613 EventListener cur = prev.m_Next;
2614 if (cur == null)
2615 break;
2616 if (cur == this)
2618 // Found our Listener, remove references to to it in the eventSources
2619 prev.m_Next = cur.m_Next; // Remove entry.
2620 RemoveReferencesToListenerInEventSources(cur);
2621 break;
2623 prev = cur;
2627 Validate();
2630 // We don't expose a Dispose(bool), because the contract is that you don't have any non-syncronous
2631 // 'cleanup' associated with this object
2633 /// Enable all events from the eventSource identified by 'eventSource' to the current dispatcher that have a
2634 /// verbosity level of 'level' or lower.
2635 ///
2636 /// This call can have the effect of REDUCING the number of events sent to the dispatcher if 'level'
2637 /// indicates a less verbose level than was previously enabled.
2638 ///
2639 /// This call never has an effect on other EventListeners.
2641 /// Returns 'true' if any eventSource could be found that matches 'eventSourceGuid'
2642 /// </summary>
2643 public void EnableEvents(EventSource eventSource, EventLevel level)
2645 EnableEvents(eventSource, level, EventKeywords.None);
2647 /// <summary>
2648 /// Enable all events from the eventSource identified by 'eventSourceGuid' to the current dispatcher that have a
2649 /// verbosity level of 'level' or lower and have a event keyword matching any of the bits in
2650 /// 'machAnyKeyword'.
2651 ///
2652 /// This call can have the effect of REDUCING the number of events sent to the dispatcher if 'level'
2653 /// indicates a less verbose level than was previously enabled or if 'machAnyKeyword' has fewer
2654 /// keywords set than where previously set.
2655 ///
2656 /// If eventSourceGuid is Guid.Empty, then the affects all eventSources in the appdomain
2657 ///
2658 /// If eventSourceGuid is not Guid.Empty, this call has no effect on any other eventSources in the appdomain.
2659 ///
2660 /// This call never has an effect on other EventListeners.
2661 ///
2662 /// Returns 'true' if any eventSource could be found that matches 'eventSourceGuid'
2663 /// </summary>
2664 public void EnableEvents(EventSource eventSource, EventLevel level, EventKeywords matchAnyKeyword)
2666 EnableEvents(eventSource, level, matchAnyKeyword, null);
2668 /// <summary>
2669 /// Enable all events from the eventSource identified by 'eventSource' to the current dispatcher that have a
2670 /// verbosity level of 'level' or lower and have a event keyword matching any of the bits in
2671 /// 'machAnyKeyword' as well as any (eventSource specific) effect passing addingional 'key-value' arguments
2672 /// 'arguments' might have.
2673 ///
2674 /// This call can have the effect of REDUCING the number of events sent to the dispatcher if 'level'
2675 /// indicates a less verbose level than was previously enabled or if 'machAnyKeyword' has fewer
2676 /// keywords set than where previously set.
2677 ///
2678 /// This call never has an effect on other EventListeners.
2679 /// </summary>
2680 public void EnableEvents(EventSource eventSource, EventLevel level, EventKeywords matchAnyKeyword, IDictionary<string, string> arguments)
2682 if (eventSource == null)
2684 throw new ArgumentNullException("eventSource");
2686 Contract.EndContractBlock();
2688 eventSource.SendCommand(this, 0, 0, EventCommand.Update, true, level, matchAnyKeyword, arguments);
2690 /// <summary>
2691 /// Disables all events coming from eventSource identified by 'eventSource'.
2692 ///
2693 /// If eventSourceGuid is Guid.Empty, then the affects all eventSources in the appdomain
2694 ///
2695 /// This call never has an effect on other EventListeners.
2696 /// </summary>
2697 public void DisableEvents(EventSource eventSource)
2699 if (eventSource == null)
2701 throw new ArgumentNullException("eventSource");
2703 Contract.EndContractBlock();
2705 eventSource.SendCommand(this, 0, 0, EventCommand.Update, false, EventLevel.LogAlways, EventKeywords.None, null);
2708 /// <summary>
2709 /// This method is caleld whenever a new eventSource is 'attached' to the dispatcher.
2710 /// This can happen for all existing EventSources when the EventListener is created
2711 /// as well as for any EventSources that come into existance after the EventListener
2712 /// has been created.
2713 ///
2714 /// These 'catch up' events are called during the construction of the EventListener.
2715 /// Subclasses need to be prepared for that.
2716 ///
2717 /// In a multi-threaded environment, it is possible that 'OnEventWritten' callbacks
2718 /// for a paritcular eventSource to occur BEFORE the OnEventSourceCreated is issued.
2719 /// </summary>
2720 /// <param name="eventSource"></param>
2721 internal protected virtual void OnEventSourceCreated(EventSource eventSource) { }
2722 /// <summary>
2723 /// This method is called whenever an event has been written by a EventSource for which the EventListener
2724 /// has enabled events.
2725 /// </summary>
2726 internal protected abstract void OnEventWritten(EventWrittenEventArgs eventData);
2727 /// <summary>
2728 /// EventSourceIndex is small non-negative integer (suitable for indexing in an array)
2729 /// identifying EventSource. It is unique per-appdomain. Some EventListeners might find
2730 /// it useful to store addditional information about each eventSource connected to it,
2731 /// and EventSourceIndex allows this extra infomation to be efficiently stored in a
2732 /// (growable) array (eg List(T)).
2733 /// </summary>
2734 static protected int EventSourceIndex(EventSource eventSource) { return eventSource.m_id; }
2736 #region private
2737 /// <summary>
2738 /// This routine adds newEventSource to the global list of eventSources, it also assigns the
2739 /// ID to the eventSource (which is simply the oridinal in the global list).
2740 ///
2741 /// EventSources currently do not pro-actively remove themselves from this list. Instead
2742 /// when eventSources's are GCed, the weak handle in this list naturally gets nulled, and
2743 /// we will reuse the slot. Today this list never shrinks (but we do reuse entries
2744 /// that are in the list). This seems OK since the expectation is that EventSources
2745 /// tend to live for the lifetime of the appdomain anyway (they tend to be used in
2746 /// global variables).
2747 /// </summary>
2748 /// <param name="newEventSource"></param>
2749 internal static void AddEventSource(EventSource newEventSource)
2751 lock (EventListenersLock)
2753 if (s_EventSources == null)
2754 s_EventSources = new List<WeakReference>(2);
2756 // Periodically search the list for existing entries to reuse, this avoids
2757 // unbounded memory use if we keep recycling eventSources (an unlikely thing).
2758 int newIndex = -1;
2759 if (s_EventSources.Count % 64 == 63) // on every block of 64, fill up the block before continuing
2761 int i = s_EventSources.Count; // Work from the top down.
2762 while (0 < i)
2764 --i;
2765 WeakReference weakRef = s_EventSources[i];
2766 if (!weakRef.IsAlive)
2768 newIndex = i;
2769 weakRef.Target = newEventSource;
2770 break;
2774 if (newIndex < 0)
2776 newIndex = s_EventSources.Count;
2777 s_EventSources.Add(new WeakReference(newEventSource));
2779 newEventSource.m_id = newIndex;
2781 // Add every existing dispatcher to the new EventSource
2782 for (EventListener listener = s_Listeners; listener != null; listener = listener.m_Next)
2783 newEventSource.AddListener(listener);
2785 Validate();
2789 /// <summary>
2790 /// Helper used in code:Dispose that removes any references to 'listenerToRemove' in any of the
2791 /// eventSources in the appdomain.
2792 ///
2793 /// The EventListenersLock must be held before calling this routine.
2794 /// </summary>
2795 private static void RemoveReferencesToListenerInEventSources(EventListener listenerToRemove)
2797 // Foreach existing EventSource in the appdomain
2798 foreach (WeakReference eventSourceRef in s_EventSources)
2800 EventSource eventSource = eventSourceRef.Target as EventSource;
2801 if (eventSource != null)
2803 // Is the first output dispatcher the dispatcher we are removing?
2804 if (eventSource.m_Dispatchers.m_Listener == listenerToRemove)
2805 eventSource.m_Dispatchers = eventSource.m_Dispatchers.m_Next;
2806 else
2808 // Remove 'listenerToRemove' from the eventSource.m_Dispatchers linked list.
2809 EventDispatcher prev = eventSource.m_Dispatchers;
2810 for (; ; )
2812 EventDispatcher cur = prev.m_Next;
2813 if (cur == null)
2815 Contract.Assert(false, "EventSource did not have a registered EventListener!");
2816 break;
2818 if (cur.m_Listener == listenerToRemove)
2820 prev.m_Next = cur.m_Next; // Remove entry.
2821 break;
2823 prev = cur;
2830 /// <summary>
2831 /// Checks internal consistancy of EventSources/Listeners.
2832 /// </summary>
2833 [Conditional("DEBUG")]
2834 internal static void Validate()
2836 lock (EventListenersLock)
2838 // Get all listeners
2839 Dictionary<EventListener, bool> allListeners = new Dictionary<EventListener, bool>();
2840 EventListener cur = s_Listeners;
2841 while (cur != null)
2843 allListeners.Add(cur, true);
2844 cur = cur.m_Next;
2847 // For all eventSources
2848 int id = -1;
2849 foreach (WeakReference eventSourceRef in s_EventSources)
2851 id++;
2852 EventSource eventSource = eventSourceRef.Target as EventSource;
2853 if (eventSource == null)
2854 continue;
2855 Contract.Assert(eventSource.m_id == id, "Unexpected event source ID.");
2857 // None listeners on eventSources exist in the dispatcher list.
2858 EventDispatcher dispatcher = eventSource.m_Dispatchers;
2859 while (dispatcher != null)
2861 Contract.Assert(allListeners.ContainsKey(dispatcher.m_Listener), "EventSource has a listener not on the global list.");
2862 dispatcher = dispatcher.m_Next;
2865 // Every dispatcher is on Dispatcher List of every eventSource.
2866 foreach (EventListener listener in allListeners.Keys)
2868 dispatcher = eventSource.m_Dispatchers;
2869 for (; ; )
2871 Contract.Assert(dispatcher != null, "Listener is not on all eventSources.");
2872 if (dispatcher.m_Listener == listener)
2873 break;
2874 dispatcher = dispatcher.m_Next;
2881 /// <summary>
2882 /// Gets a global lock that is intended to protect the code:s_Listeners linked list and the
2883 /// code:s_EventSources WeakReference list. (We happen to use the s_EventSources list as
2884 /// the lock object)
2885 /// </summary>
2886 internal static object EventListenersLock
2890 if (s_EventSources == null)
2891 Interlocked.CompareExchange(ref s_EventSources, new List<WeakReference>(2), null);
2892 return s_EventSources;
2896 // Instance fields
2897 internal volatile EventListener m_Next; // These form a linked list in s_Listeners
2898 #if FEATURE_ACTIVITYSAMPLING
2899 internal ActivityFilter m_activityFilter; // If we are filtering by activity on this Listener, this keeps track of it.
2900 #endif // FEATURE_ACTIVITYSAMPLING
2902 // static fields
2903 internal static EventListener s_Listeners; // list of all EventListeners in the appdomain
2904 internal static List<WeakReference> s_EventSources; // all EventSources in the appdomain
2905 #endregion
2908 /// <summary>
2909 /// Passed to the code:EventSource.OnEventCommand callback
2910 /// </summary>
2911 public class EventCommandEventArgs : EventArgs
2913 public EventCommand Command { get; private set; }
2914 public IDictionary<String, String> Arguments { get; private set; }
2916 public bool EnableEvent(int eventId)
2918 if (Command != EventCommand.Enable && Command != EventCommand.Disable)
2919 throw new InvalidOperationException();
2920 return eventSource.EnableEventForDispatcher(dispatcher, eventId, true);
2922 public bool DisableEvent(int eventId)
2924 if (Command != EventCommand.Enable && Command != EventCommand.Disable)
2925 throw new InvalidOperationException();
2926 return eventSource.EnableEventForDispatcher(dispatcher, eventId, false);
2929 #region private
2931 internal EventCommandEventArgs(EventCommand command, IDictionary<string, string> arguments, EventSource eventSource, EventDispatcher dispatcher)
2933 this.Command = command;
2934 this.Arguments = arguments;
2935 this.eventSource = eventSource;
2936 this.dispatcher = dispatcher;
2939 internal EventSource eventSource;
2940 internal EventDispatcher dispatcher;
2942 #endregion
2945 /// <summary>
2946 /// code:EventWrittenEventArgs is passed when the callback given in code:EventListener.OnEventWritten is
2947 /// fired.
2948 /// </summary>
2949 public class EventWrittenEventArgs : EventArgs
2951 public int EventId { get; internal set; }
2952 public Guid ActivityId
2954 [System.Security.SecurityCritical]
2955 get { return EventSource.CurrentThreadActivityId; }
2957 public Guid RelatedActivityId
2959 [System.Security.SecurityCritical]
2960 get;
2961 internal set;
2963 public ReadOnlyCollection<Object> Payload { get; internal set; }
2964 public EventSource EventSource { get { return m_eventSource; } }
2965 public EventKeywords Keywords { get { return (EventKeywords)m_eventSource.m_eventData[EventId].Descriptor.Keywords; } }
2966 public EventOpcode Opcode { get { return (EventOpcode)m_eventSource.m_eventData[EventId].Descriptor.Opcode; } }
2967 public EventTask Task { get { return (EventTask)m_eventSource.m_eventData[EventId].Descriptor.Task; } }
2968 public string Message
2970 get
2972 if (m_message != null)
2973 return m_message;
2974 else
2975 return m_eventSource.m_eventData[EventId].Message;
2977 internal set
2979 m_message = value;
2983 #if FEATURE_MANAGED_ETW_CHANNELS
2984 public EventChannel Channel { get { return (EventChannel) m_eventSource.m_eventData[EventId].Descriptor.Channel; }}
2985 #endif
2986 public byte Version { get { return m_eventSource.m_eventData[EventId].Descriptor.Version; } }
2987 public EventLevel Level
2991 if ((uint)EventId >= (uint)m_eventSource.m_eventData.Length)
2992 return EventLevel.LogAlways;
2993 return (EventLevel)m_eventSource.m_eventData[EventId].Descriptor.Level;
2997 #region private
2998 internal EventWrittenEventArgs(EventSource eventSource)
3000 m_eventSource = eventSource;
3002 private string m_message;
3003 private EventSource m_eventSource;
3004 #endregion
3007 [AttributeUsage(AttributeTargets.Class)]
3008 public sealed class EventSourceAttribute : Attribute
3010 public string Name { get; set; }
3011 public string Guid { get; set; }
3013 /// <summary>
3014 /// EventSources support localization of events. The names used for events, opcodes, tasks, keyworks and maps
3015 /// can be localized to several languages if desired. This works by creating a ResX style string table
3016 /// (by simply adding a 'Resource File' to your project). This resource file is given a name e.g.
3017 /// 'DefaultNameSpace.ResourceFileName' which can be passed to the ResourceManager constructor to read the
3018 /// resoruces. This name is the value of the LocalizationResources property.
3019 ///
3020 /// LocalizationResources property is non-null, then EventSource will look up the localized strings for events by
3021 /// using the following resource naming scheme
3022 ///
3023 /// event_EVENTNAME
3024 /// task_TASKNAME
3025 /// keyword_KEYWORDNAME
3026 /// map_MAPNAME
3027 ///
3028 /// where the capitpalized name is the name of the event, task, keywork, or map value that should be localized.
3029 /// Note that the localized string for an event corresponds to the Messsage string, and can have {0} values
3030 /// which represent the payload values.
3031 /// </summary>
3032 public string LocalizationResources { get; set; }
3035 /// <summary>
3036 /// None instance methods in a class that subclasses code:EventSource that and return void are
3037 /// assumed by default to be methods that generate an event. Enough information can be deduced from the
3038 /// name of the method and its signature to generate basic schema information for the event. The
3039 /// code:EventAttribute allows you to specify additional event schema information for an event if
3040 /// desired.
3041 /// </summary>
3042 [AttributeUsage(AttributeTargets.Method)]
3043 public sealed class EventAttribute : Attribute
3045 public EventAttribute(int eventId) { this.EventId = eventId; Level = EventLevel.Informational; }
3046 public int EventId { get; private set; }
3047 public EventLevel Level { get; set; }
3048 public EventKeywords Keywords { get; set; }
3049 public EventOpcode Opcode { get; set; }
3050 public EventTask Task { get; set; }
3051 #if FEATURE_MANAGED_ETW_CHANNELS
3052 public EventChannel Channel { get; set; }
3053 #endif
3054 public byte Version { get; set; }
3056 /// <summary>
3057 /// This is also used for TraceSource compatabilty. If code:EventSource.TraceSourceSupport is
3058 /// on events will also be logged a tracesource with the same name as the eventSource. If this
3059 /// property is set then the payload will go to code:TraceSource.TraceEvent, and this string
3060 /// will be used as the message. If this property is not set not set it goes to
3061 /// code:TraceSource.TraceData. You can use standard .NET substitution operators (eg {1}) in
3062 /// the string and they will be replaced with the 'ToString()' of the cooresponding part of the
3063 /// event payload.
3064 /// </summary>
3065 public string Message { get; set; }
3068 /// <summary>
3069 /// By default all instance methods in a class that subclasses code:EventSource that and return
3070 /// void are assumed to be methods that generate an event. This default can be overriden by specifying
3071 /// the code:NonEventAttribute
3072 /// </summary>
3073 [AttributeUsage(AttributeTargets.Method)]
3074 public sealed class NonEventAttribute : Attribute
3076 public NonEventAttribute() { }
3081 #if FEATURE_MANAGED_ETW_CHANNELS
3082 [AttributeUsage(AttributeTargets.Field)]
3083 public class ChannelAttribute : Attribute
3085 public bool Enabled { get; set; }
3086 public string Isolation { get; set; }
3087 /// <summary>
3088 /// Legal values are in ChannelTypes
3089 /// </summary>
3090 public string Type { get; set; }
3093 public string ImportChannel { get; set; }
3100 public static class ChannelTypes
3102 public const string Admin = "Admin";
3103 public const string Operational = "Operational";
3104 public const string Analytic = "Analytic";
3105 public const string Debug = "Debug";
3107 #endif
3109 public enum EventCommand
3111 Update = 0,
3112 SendManifest = -1,
3113 Enable = -2,
3114 Disable = -3
3118 #region private classes
3120 #if FEATURE_ACTIVITYSAMPLING
3122 /// <summary>
3123 /// ActivityFilter is a helper structure that is used to keep track of run-time state
3124 /// associated with activity filtering. It is 1-1 with EventListeners (logically
3125 /// every listener has one of these, however we actually allocate them lazily), as well
3126 /// as 1-to-1 with tracing-aware EtwSessions.
3127 ///
3128 /// This structure also keeps track of the sampling counts associated with 'trigger'
3129 /// events. Because these trigger events are rare, and you typically only have one of
3130 /// them, we store them here as a linked list.
3131 /// </summary>
3132 internal sealed class ActivityFilter : IDisposable
3134 /// <summary>
3135 /// Disable all activity filtering for the listener associated with 'filterList',
3136 /// (in the session associated with it) that is triggered by any event in 'source'.
3137 /// </summary>
3138 public static void DisableFilter(ref ActivityFilter filterList, EventSource source)
3140 Contract.Assert(Monitor.IsEntered(EventListener.EventListenersLock));
3142 if (filterList == null)
3143 return;
3145 ActivityFilter cur;
3146 // Remove it from anywhere in the list (except the first element, which has to
3147 // be treated specially)
3148 ActivityFilter prev = filterList;
3149 cur = prev.m_next;
3150 while (cur != null)
3152 if (cur.m_providerGuid == source.Guid)
3154 // update TriggersActivityTracking bit
3155 if (cur.m_eventId >= 0 && cur.m_eventId < source.m_eventData.Length)
3156 --source.m_eventData[cur.m_eventId].TriggersActivityTracking;
3158 // Remove it from the linked list.
3159 prev.m_next = cur.m_next;
3160 // dispose of the removed node
3161 cur.Dispose();
3162 // update cursor
3163 cur = prev.m_next;
3165 else
3167 // update cursors
3168 prev = cur;
3169 cur = prev.m_next;
3173 // Sadly we have to treat the first element specially in linked list removal in C#
3174 if (filterList.m_providerGuid == source.Guid)
3176 // update TriggersActivityTracking bit
3177 if (filterList.m_eventId >= 0 && filterList.m_eventId < source.m_eventData.Length)
3178 --source.m_eventData[filterList.m_eventId].TriggersActivityTracking;
3180 // We are the first element in the list.
3181 var first = filterList;
3182 filterList = first.m_next;
3183 // dispose of the removed node
3184 first.Dispose();
3186 // the above might have removed the one ActivityFilter in the session that contains the
3187 // cleanup delegate; re-create the delegate if needed
3188 if (filterList != null)
3190 EnsureActivityCleanupDelegate(filterList);
3194 /// <summary>
3195 /// Currently this has "override" semantics. We first disable all filters
3196 /// associated with 'source', and next we add new filters for each entry in the
3197 /// string 'startEvents'. participateInSampling specifies whether non-startEvents
3198 /// always trigger or only trigger when current activity is 'active'.
3199 /// </summary>
3200 public static void UpdateFilter(
3201 ref ActivityFilter filterList,
3202 EventSource source,
3203 int perEventSourceSessionId,
3204 string startEvents)
3206 Contract.Assert(Monitor.IsEntered(EventListener.EventListenersLock));
3208 // first remove all filters associated with 'source'
3209 DisableFilter(ref filterList, source);
3211 if (!string.IsNullOrEmpty(startEvents))
3213 // ActivitySamplingStartEvents is a space-separated list of Event:Frequency pairs.
3214 // The Event may be specified by name or by ID. Errors in parsing such a pair
3215 // result in the error being reported to the listeners, and the pair being ignored.
3216 // E.g. "CustomActivityStart:1000 12:10" specifies that for event CustomActivityStart
3217 // we should initiate activity tracing once every 1000 events, *and* for event ID 12
3218 // we should initiate activity tracing once every 10 events.
3219 string[] activityFilterStrings = startEvents.Split(' ');
3221 for (int i = 0; i < activityFilterStrings.Length; ++i)
3223 string activityFilterString = activityFilterStrings[i];
3224 int sampleFreq = 1;
3225 int eventId = -1;
3226 int colonIdx = activityFilterString.IndexOf(':');
3227 if (colonIdx < 0)
3229 source.ReportOutOfBandMessage("ERROR: Invalid ActivitySamplingStartEvent specification: " +
3230 activityFilterString, false);
3231 // ignore failure...
3232 continue;
3234 string sFreq = activityFilterString.Substring(colonIdx + 1);
3235 if (!int.TryParse(sFreq, out sampleFreq))
3237 source.ReportOutOfBandMessage("ERROR: Invalid sampling frequency specification: " + sFreq, false);
3238 continue;
3240 activityFilterString = activityFilterString.Substring(0, colonIdx);
3241 if (!int.TryParse(activityFilterString, out eventId))
3243 // reset eventId
3244 eventId = -1;
3245 // see if it's an event name
3246 for (int j = 0; j < source.m_eventData.Length; j++)
3248 EventSource.EventMetadata[] ed = source.m_eventData;
3249 if (ed[j].Name != null && ed[j].Name.Length == activityFilterString.Length &&
3250 string.Compare(ed[j].Name, activityFilterString, StringComparison.OrdinalIgnoreCase) == 0)
3252 eventId = ed[j].Descriptor.EventId;
3253 break;
3257 if (eventId < 0 || eventId >= source.m_eventData.Length)
3259 source.ReportOutOfBandMessage("ERROR: Invalid eventId specification: " + activityFilterString, false);
3260 continue;
3262 EnableFilter(ref filterList, source, perEventSourceSessionId, eventId, sampleFreq);
3267 /// <summary>
3268 /// Returns the first ActivityFilter from 'filterList' corresponding to 'source'.
3269 /// </summary>
3270 public static ActivityFilter GetFilter(ActivityFilter filterList, EventSource source)
3272 for (var af = filterList; af != null; af = af.m_next)
3274 if (af.m_providerGuid == source.Guid && af.m_samplingFreq != -1)
3275 return af;
3277 return null;
3280 /// <summary>
3281 /// Returns a session mask representing all sessions in which the activity
3282 /// associated with the current thread is allowed through the activity filter.
3283 /// If 'triggeringEvent' is true the event MAY be a triggering event. Ideally
3284 /// most of the time this is false as you can guarentee this event is NOT a
3285 /// triggering event. If 'triggeringEvent' is true, then it checks the
3286 /// 'EventSource' and 'eventID' of the event being logged to see if it is actually
3287 /// a trigger. If so it activates the current activity.
3288 ///
3289 /// If 'childActivityID' is present, it will be added to the active set if the
3290 /// current activity is active.
3291 /// </summary>
3292 [SecurityCritical]
3293 unsafe public static bool PassesActivityFilter(
3294 ActivityFilter filterList,
3295 Guid* childActivityID,
3296 bool triggeringEvent,
3297 EventSource source,
3298 int eventId)
3300 Contract.Assert(filterList != null && filterList.m_activeActivities != null);
3301 bool shouldBeLogged = false;
3302 if (triggeringEvent)
3304 for (ActivityFilter af = filterList; af != null; af = af.m_next)
3306 if (eventId == af.m_eventId && source.Guid == af.m_providerGuid)
3308 // Update the sampling count with wrap-around
3309 int curSampleCount, newSampleCount;
3312 curSampleCount = af.m_curSampleCount;
3313 if (curSampleCount <= 1)
3314 newSampleCount = af.m_samplingFreq; // Wrap around, counting down to 1
3315 else
3316 newSampleCount = curSampleCount - 1;
3318 while (Interlocked.CompareExchange(ref af.m_curSampleCount, newSampleCount, curSampleCount) != curSampleCount);
3319 // If we hit zero, then start tracking the activity.
3320 if (curSampleCount <= 1)
3322 Guid currentActivityId = EventSource.InternalCurrentThreadActivityId;
3323 Tuple<Guid, int> startId;
3324 // only add current activity if it's not already a root activity
3325 if (!af.m_rootActiveActivities.TryGetValue(currentActivityId, out startId))
3327 // EventSource.OutputDebugString(string.Format(" PassesAF - Triggering(session {0}, evt {1})", af.m_perEventSourceSessionId, eventId));
3328 shouldBeLogged = true;
3329 af.m_activeActivities[currentActivityId] = Environment.TickCount;
3330 af.m_rootActiveActivities[currentActivityId] = Tuple.Create(source.Guid, eventId);
3333 else
3335 // a start event following a triggering start event
3336 Guid currentActivityId = EventSource.InternalCurrentThreadActivityId;
3337 Tuple<Guid, int> startId;
3338 // only remove current activity if we added it
3339 if (af.m_rootActiveActivities.TryGetValue(currentActivityId, out startId) &&
3340 startId.Item1 == source.Guid && startId.Item2 == eventId)
3342 // EventSource.OutputDebugString(string.Format("Activity dying: {0} -> StartEvent({1})", currentActivityId, eventId));
3343 // remove activity only from current logging scope (af)
3344 int dummy;
3345 af.m_activeActivities.TryRemove(currentActivityId, out dummy);
3348 break;
3353 var activeActivities = GetActiveActivities(filterList);
3354 if (activeActivities != null)
3356 // if we hadn't already determined this should be logged, test further
3357 if (!shouldBeLogged)
3359 shouldBeLogged = !activeActivities.IsEmpty &&
3360 activeActivities.ContainsKey(EventSource.InternalCurrentThreadActivityId);
3362 if (shouldBeLogged && childActivityID != null &&
3363 ((EventOpcode)source.m_eventData[eventId].Descriptor.Opcode == EventOpcode.Send))
3365 FlowActivityIfNeeded(filterList, null, childActivityID);
3366 // EventSource.OutputDebugString(string.Format(" PassesAF - activity {0}", *childActivityID));
3369 // EventSource.OutputDebugString(string.Format(" PassesAF - shouldBeLogged(evt {0}) = {1:x}", eventId, shouldBeLogged));
3370 return shouldBeLogged;
3373 [System.Security.SecuritySafeCritical]
3374 public static bool IsCurrentActivityActive(ActivityFilter filterList)
3376 var activeActivities = GetActiveActivities(filterList);
3377 if (activeActivities != null &&
3378 activeActivities.ContainsKey(EventSource.InternalCurrentThreadActivityId))
3379 return true;
3381 return false;
3384 /// <summary>
3385 /// For the EventListener/EtwSession associated with 'filterList', add 'childActivityid'
3386 /// to list of active activities IF 'currentActivityId' is also active. Passing in a null
3387 /// value for 'currentActivityid' is an indication tha caller has already verified
3388 /// that the current activity is active.
3389 /// </summary>
3390 [SecurityCritical]
3391 unsafe public static void FlowActivityIfNeeded(ActivityFilter filterList, Guid *currentActivityId, Guid *childActivityID)
3393 Contract.Assert(childActivityID != null);
3395 var activeActivities = GetActiveActivities(filterList);
3396 Contract.Assert(activeActivities != null);
3398 // take currentActivityId == null to mean we *know* the current activity is "active"
3399 if (currentActivityId != null && !activeActivities.ContainsKey(*currentActivityId))
3400 return;
3402 if (activeActivities.Count > MaxActivityTrackCount)
3404 TrimActiveActivityStore(activeActivities);
3405 // make sure current activity is still in the set:
3406 activeActivities[EventSource.InternalCurrentThreadActivityId] = Environment.TickCount;
3408 // add child activity to list of actives
3409 activeActivities[*childActivityID] = Environment.TickCount;
3413 /// <summary>
3414 /// </summary>
3415 public static void UpdateKwdTriggers(ActivityFilter activityFilter, Guid sourceGuid, EventSource source, EventKeywords sessKeywords)
3417 for (var af = activityFilter; af != null; af = af.m_next)
3419 if ((sourceGuid == af.m_providerGuid) &&
3420 (source.m_eventData[af.m_eventId].TriggersActivityTracking > 0 ||
3421 ((EventOpcode)source.m_eventData[af.m_eventId].Descriptor.Opcode == EventOpcode.Send)))
3423 // we could be more precise here, if we tracked 'anykeywords' per session
3424 source.m_keywordTriggers |= (source.m_eventData[af.m_eventId].Descriptor.Keywords & (long)sessKeywords);
3429 /// <summary>
3430 /// For the EventSource specified by 'sourceGuid' and the EventListener/EtwSession
3431 /// associated with 'this' ActivityFilter list, return configured sequence of
3432 /// [eventId, sampleFreq] pairs that defines the sampling policy.
3433 /// </summary>
3434 public IEnumerable<Tuple<int, int>> GetFilterAsTuple(Guid sourceGuid)
3436 for (ActivityFilter af = this; af != null; af = af.m_next)
3438 if (af.m_providerGuid == sourceGuid)
3439 yield return Tuple.Create(af.m_eventId, af.m_samplingFreq);
3443 /// <summary>
3444 /// The cleanup being performed consists of removing the m_myActivityDelegate from
3445 /// the static s_activityDying, therefore allowing the ActivityFilter to be reclaimed.
3446 /// </summary>
3447 public void Dispose()
3449 Contract.Assert(Monitor.IsEntered(EventListener.EventListenersLock));
3450 // m_myActivityDelegate is still alive (held by the static EventSource.s_activityDying).
3451 // Therefore we are ok to take a dependency on m_myActivityDelegate being valid even
3452 // during the finalization of the ActivityFilter
3453 if (m_myActivityDelegate != null)
3455 EventSource.s_activityDying = (Action<Guid>)Delegate.Remove(EventSource.s_activityDying, m_myActivityDelegate);
3456 m_myActivityDelegate = null;
3460 #region private
3462 /// <summary>
3463 /// Creates a new ActivityFilter that is triggered by 'eventId' from 'source' ever
3464 /// 'samplingFreq' times the event fires. You can have several of these forming a
3465 /// linked list.
3466 /// </summary>
3467 private ActivityFilter(EventSource source, int perEventSourceSessionId, int eventId, int samplingFreq, ActivityFilter existingFilter = null)
3469 m_providerGuid = source.Guid;
3470 m_perEventSourceSessionId = perEventSourceSessionId;
3471 m_eventId = eventId;
3472 m_samplingFreq = samplingFreq;
3473 m_next = existingFilter;
3475 Contract.Assert(existingFilter == null ||
3476 (existingFilter.m_activeActivities == null) == (existingFilter.m_rootActiveActivities == null));
3478 // if this is the first filter we add for this session, we need to create a new
3479 // table of activities. m_activeActivities is common across EventSources in the same
3480 // session
3481 ConcurrentDictionary<Guid, int> activeActivities = null;
3482 if (existingFilter == null ||
3483 (activeActivities = GetActiveActivities(existingFilter)) == null)
3485 m_activeActivities = new ConcurrentDictionary<Guid, int>();
3486 m_rootActiveActivities = new ConcurrentDictionary<Guid, Tuple<Guid, int>>();
3488 // Add a delegate to the 'SetCurrentThreadToActivity callback so that I remove 'dead' activities
3489 m_myActivityDelegate = GetActivityDyingDelegate(this);
3490 EventSource.s_activityDying = (Action<Guid>)Delegate.Combine(EventSource.s_activityDying, m_myActivityDelegate);
3492 else
3494 m_activeActivities = activeActivities;
3495 m_rootActiveActivities = existingFilter.m_rootActiveActivities;
3500 /// <summary>
3501 /// Ensure there's at least one ActivityFilter in the 'filterList' that contains an
3502 /// activity-removing delegate for the listener/session associated with 'filterList'.
3503 /// </summary>
3504 private static void EnsureActivityCleanupDelegate(ActivityFilter filterList)
3506 if (filterList == null)
3507 return;
3509 for (ActivityFilter af = filterList; af != null; af = af.m_next)
3511 if (af.m_myActivityDelegate != null)
3512 return;
3515 // we didn't find a delegate
3516 filterList.m_myActivityDelegate = GetActivityDyingDelegate(filterList);
3517 EventSource.s_activityDying = (Action<Guid>)Delegate.Combine(EventSource.s_activityDying, filterList.m_myActivityDelegate);
3520 /// <summary>
3521 /// Builds the delegate to be called when an activity is dying. This is responsible
3522 /// for performing whatever cleanup is needed for the ActivityFilter list passed in.
3523 /// This gets "added" to EventSource.s_activityDying and ends up being called from
3524 /// EventSource.SetCurrentThreadActivityId and ActivityFilter.PassesActivityFilter.
3525 /// </summary>
3526 /// <returns>The delegate to be called when an activity is dying</returns>
3527 private static Action<Guid> GetActivityDyingDelegate(ActivityFilter filterList)
3529 return (Guid oldActivity) =>
3531 int dummy;
3532 filterList.m_activeActivities.TryRemove(oldActivity, out dummy);
3533 Tuple<Guid, int> dummyTuple;
3534 filterList.m_rootActiveActivities.TryRemove(oldActivity, out dummyTuple);
3538 /// <summary>
3539 /// Enables activity filtering for the listener associated with 'filterList', triggering on
3540 /// the event 'eventID' from 'source' with a sampling frequency of 'samplingFreq'
3541 ///
3542 /// if 'eventID' is out of range (e.g. negative), it means we are not triggering (but we are
3543 /// activitySampling if something else triggered).
3544 /// </summary>
3545 /// <returns>true if activity sampling is enabled the samplingFreq is non-zero </returns>
3546 private static bool EnableFilter(ref ActivityFilter filterList, EventSource source, int perEventSourceSessionId, int eventId, int samplingFreq)
3548 Contract.Assert(Monitor.IsEntered(EventListener.EventListenersLock));
3549 Contract.Assert(samplingFreq > 0);
3550 Contract.Assert(eventId >= 0);
3552 filterList = new ActivityFilter(source, perEventSourceSessionId, eventId, samplingFreq, filterList);
3554 // Mark the 'quick Check' that indicates this is a trigger event.
3555 // If eventId is out of range then this mark is not done which has the effect of ignoring
3556 // the trigger.
3557 if (0 <= eventId && eventId < source.m_eventData.Length)
3558 ++source.m_eventData[eventId].TriggersActivityTracking;
3560 return true;
3563 /// <summary>
3564 /// Normally this code never runs, it is here just to prevent run-away resource usage.
3565 /// </summary>
3566 private static void TrimActiveActivityStore(ConcurrentDictionary<Guid, int> activities)
3568 if (activities.Count > MaxActivityTrackCount)
3570 // Remove half of the oldest activity ids.
3571 var keyValues = activities.ToArray();
3572 var tickNow = Environment.TickCount;
3574 // Sort by age, taking into account wrap-around. As long as x and y are within
3575 // 23 days of now then (0x7FFFFFFF & (tickNow - x.Value)) is the delta (even if
3576 // TickCount wraps). I then sort by DESCENDING age. (that is oldest value first)
3577 Array.Sort(keyValues, (x, y) => (0x7FFFFFFF & (tickNow - y.Value)) - (0x7FFFFFFF & (tickNow - x.Value)));
3578 for (int i = 0; i < keyValues.Length / 2; i++)
3580 int dummy;
3581 activities.TryRemove(keyValues[i].Key, out dummy);
3586 private static ConcurrentDictionary<Guid, int> GetActiveActivities(
3587 ActivityFilter filterList)
3589 for (ActivityFilter af = filterList; af != null; af = af.m_next)
3591 if (af.m_activeActivities != null)
3592 return af.m_activeActivities;
3594 return null;
3597 // m_activeActivities always points to the sample dictionary for EVERY ActivityFilter
3598 // in the m_next list. The 'int' value in the m_activities set is a timestamp
3599 // (Environment.TickCount) of when the entry was put in the system and is used to
3600 // remove 'old' entries that if the set gets too big.
3601 ConcurrentDictionary<Guid, int> m_activeActivities;
3603 // m_rootActiveActivities holds the "root" active activities, i.e. the activities
3604 // that were marked as active because a Start event fired on them. We need to keep
3605 // track of these to enable sampling in the scenario of an app's main thread that
3606 // never explicitly sets distinct activity IDs as it executes. To handle these
3607 // situations we manufacture a Guid from the thread's ID, and:
3608 // (a) we consider the firing of a start event when the sampling counter reaches
3609 // zero to mark the beginning of an interesting activity, and
3610 // (b) we consider the very next firing of the same start event to mark the
3611 // ending of that activity.
3612 // We use a ConcurrentDictionary to avoid taking explicit locks.
3613 // The key (a guid) represents the activity ID of the root active activity
3614 // The value is made up of the Guid of the event provider and the eventId of
3615 // the start event.
3616 ConcurrentDictionary<Guid, Tuple<Guid, int>> m_rootActiveActivities;
3617 Guid m_providerGuid; // We use the GUID rather than object identity because we don't want to keep the eventSource alive
3618 int m_eventId; // triggering event
3619 int m_samplingFreq; // Counter reset to this when it hits 0
3620 int m_curSampleCount; // We count down to 0 and then activate the activity.
3621 int m_perEventSourceSessionId; // session ID bit for ETW, 0 for EventListeners
3623 const int MaxActivityTrackCount = 100000; // maximum number of tracked activities
3625 ActivityFilter m_next; // We create a linked list of these
3626 Action<Guid> m_myActivityDelegate;
3627 #endregion
3631 /// <summary>
3632 /// An EtwSession instance represents an activity-tracing-aware ETW session. Since these
3633 /// are limited to 8 concurrent sessions per machine (currently) we're going to store
3634 /// the active ones in a singly linked list.
3635 /// </summary>
3636 internal class EtwSession
3638 public static EtwSession GetEtwSession(int etwSessionId, bool bCreateIfNeeded = false)
3640 if (etwSessionId < 0)
3641 return null;
3643 EtwSession etwSession;
3644 foreach(var wrEtwSession in s_etwSessions)
3646 if (wrEtwSession.TryGetTarget(out etwSession) && etwSession.m_etwSessionId == etwSessionId)
3647 return etwSession;
3650 if (!bCreateIfNeeded)
3651 return null;
3653 if (s_etwSessions == null)
3654 s_etwSessions = new List<WeakReference<EtwSession>>();
3656 etwSession = new EtwSession(etwSessionId);
3657 s_etwSessions.Add(new WeakReference<EtwSession>(etwSession));
3658 if (s_etwSessions.Count > s_thrSessionCount)
3659 TrimGlobalList();
3661 return etwSession;
3665 public static void RemoveEtwSession(EtwSession etwSession)
3667 Contract.Assert(etwSession != null);
3668 if (s_etwSessions == null)
3669 return;
3671 s_etwSessions.RemoveAll((wrEtwSession) =>
3673 EtwSession session;
3674 return wrEtwSession.TryGetTarget(out session) &&
3675 (session.m_etwSessionId == etwSession.m_etwSessionId);
3678 if (s_etwSessions.Count > s_thrSessionCount)
3679 TrimGlobalList();
3682 private static void TrimGlobalList()
3684 if (s_etwSessions == null)
3685 return;
3687 s_etwSessions.RemoveAll((wrEtwSession) =>
3689 EtwSession session;
3690 return !wrEtwSession.TryGetTarget(out session);
3694 private EtwSession(int etwSessionId)
3696 m_etwSessionId = etwSessionId;
3699 public readonly int m_etwSessionId; // ETW session ID (as retrieved by EventProvider)
3700 public ActivityFilter m_activityFilter; // all filters enabled for this session
3702 private static List<WeakReference<EtwSession>> s_etwSessions = new List<WeakReference<EtwSession>>();
3703 private const int s_thrSessionCount = 16;
3706 #endif // FEATURE_ACTIVITYSAMPLING
3708 // holds a bitfield representing a session mask
3709 /// <summary>
3710 /// A SessionMask represents a set of (at most MAX) sessions as a bit mask. The perEventSourceSessionId
3711 /// is the index in the SessionMask of the bit that will be set. These can translate to
3712 /// EventSource's reserved keywords bits using the provided ToEventKeywords() and
3713 /// FromEventKeywords() methods.
3714 /// </summary>
3715 internal struct SessionMask
3717 public SessionMask(SessionMask m)
3718 { m_mask = m.m_mask; }
3720 public SessionMask(uint mask = 0)
3721 { m_mask = mask & MASK; }
3723 public bool IsEqualOrSupersetOf(SessionMask m)
3725 return (this.m_mask | m.m_mask) == this.m_mask;
3728 public static SessionMask All
3730 get { return new SessionMask(MASK); }
3733 public static SessionMask FromId(int perEventSourceSessionId)
3735 Contract.Assert(perEventSourceSessionId < MAX);
3736 return new SessionMask((uint) 1 << perEventSourceSessionId);
3739 public ulong ToEventKeywords()
3741 return (ulong)m_mask << SHIFT_SESSION_TO_KEYWORD;
3744 public static SessionMask FromEventKeywords(ulong m)
3746 return new SessionMask((uint)(m >> SHIFT_SESSION_TO_KEYWORD));
3749 public bool this[int perEventSourceSessionId]
3751 get
3753 Contract.Assert(perEventSourceSessionId < MAX);
3754 return (m_mask & (1 << perEventSourceSessionId)) != 0;
3756 set
3758 Contract.Assert(perEventSourceSessionId < MAX);
3759 if (value) m_mask |= ((uint) 1 << perEventSourceSessionId);
3760 else m_mask &= ~((uint) 1 << perEventSourceSessionId);
3764 public static SessionMask operator | (SessionMask m1, SessionMask m2)
3766 return new SessionMask(m1.m_mask | m2.m_mask);
3769 public static SessionMask operator & (SessionMask m1, SessionMask m2)
3771 return new SessionMask(m1.m_mask & m2.m_mask);
3774 public static SessionMask operator ^ (SessionMask m1, SessionMask m2)
3776 return new SessionMask(m1.m_mask ^ m2.m_mask);
3779 public static SessionMask operator ~(SessionMask m)
3781 return new SessionMask(MASK & ~(m.m_mask));
3784 public static explicit operator ulong(SessionMask m)
3785 { return m.m_mask; }
3787 public static explicit operator uint(SessionMask m)
3788 { return m.m_mask; }
3790 private uint m_mask;
3792 internal const int SHIFT_SESSION_TO_KEYWORD = 44; // bits 44-47 inclusive are reserved
3793 internal const uint MASK = 0x0fU; // the mask of 4 reserved bits
3794 internal const uint MAX = 4; // maximum number of simultaneous ETW sessions supported
3795 // (equals bitcount(MASK))
3798 /// <summary>
3799 /// code:EventDispatchers are a simple 'helper' structure that holds the filtering state
3800 /// (m_EventEnabled) for a particular EventSource X EventListener tuple
3801 ///
3802 /// Thus a single EventListener may have many EventDispatchers (one for every EventSource
3803 /// that that EventListener has activate) and a Single EventSource may also have many
3804 /// event Dispatchers (one for every EventListener that has activated it).
3805 ///
3806 /// Logically a particular EventDispatcher belongs to exactly one EventSource and exactly
3807 /// one EventListener (alhtough EventDispatcher does not 'remember' the EventSource it is
3808 /// associated with.
3809 /// </summary>
3810 internal class EventDispatcher
3812 internal EventDispatcher(EventDispatcher next, bool[] eventEnabled, EventListener listener)
3814 m_Next = next;
3815 m_EventEnabled = eventEnabled;
3816 m_Listener = listener;
3819 // Instance fields
3820 readonly internal EventListener m_Listener; // The dispatcher this entry is for
3821 internal bool[] m_EventEnabled; // For every event in a the eventSource, is it enabled?
3822 #if FEATURE_ACTIVITYSAMPLING
3823 internal bool m_activityFilteringEnabled; // does THIS EventSource have activity filtering turned on for this listener?
3824 #endif // FEATURE_ACTIVITYSAMPLING
3826 // Only guarenteed to exist after a InsureInit()
3827 internal EventDispatcher m_Next; // These form a linked list in code:EventSource.m_Dispatchers
3828 // Of all listeners for that eventSource.
3831 /// <summary>
3832 /// ManifestBuilder is designed to isolate the details of the message of the event from the
3833 /// rest of EventSource. This one happens to create XML.
3834 /// </summary>
3835 internal class ManifestBuilder
3837 /// <summary>
3838 /// Build a manifest for 'providerName' with the given GUID, which will be packaged into 'dllName'.
3839 /// 'resources, is a resource manager. If specified all messsages are localized using that manager.
3840 /// </summary>
3841 public ManifestBuilder(string providerName, Guid providerGuid, string dllName, ResourceManager resources)
3843 #if FEATURE_MANAGED_ETW_CHANNELS
3844 this.providerName = providerName;
3845 #endif
3846 this.resources = resources;
3847 sb = new StringBuilder();
3848 events = new StringBuilder();
3849 templates = new StringBuilder();
3850 opcodeTab = new Dictionary<int, string>();
3851 stringTab = new Dictionary<string, string>();
3853 sb.AppendLine("<instrumentationManifest xmlns=\"http://schemas.microsoft.com/win/2004/08/events\">");
3854 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\">");
3855 sb.AppendLine(" <events xmlns=\"http://schemas.microsoft.com/win/2004/08/events\">");
3856 sb.Append("<provider name=\"").Append(providerName).
3857 Append("\" guid=\"{").Append(providerGuid.ToString()).Append("}");
3858 if (dllName != null)
3859 sb.Append("\" resourceFileName=\"").Append(dllName).Append("\" messageFileName=\"").Append(dllName);
3861 var symbolsName = providerName.Replace("-", "");
3862 sb.Append("\" symbol=\"").Append(symbolsName);
3863 sb.Append("\">").AppendLine();
3866 public void AddOpcode(string name, int value)
3868 opcodeTab[value] = name;
3870 public void AddTask(string name, int value)
3872 if (taskTab == null)
3873 taskTab = new Dictionary<int, string>();
3874 taskTab[value] = name;
3876 public void AddKeyword(string name, ulong value)
3878 if ((value & (value - 1)) != 0) // Is it a power of 2?
3879 throw new ArgumentException(Environment.GetResourceString("EventSource_KeywordNeedPowerOfTwo", value.ToString("x", CultureInfo.CurrentCulture), name));
3880 if (keywordTab == null)
3881 keywordTab = new Dictionary<ulong, string>();
3882 keywordTab[value] = name;
3885 #if FEATURE_MANAGED_ETW_CHANNELS
3886 /// <summary>
3887 /// Add a channel. channelAttribute can be null
3888 /// </summary>
3889 public void AddChannel(string name, int value, ChannelAttribute channelAttribute)
3891 if (channelTab == null)
3892 channelTab = new Dictionary<int, ChannelInfo>();
3893 channelTab[value] = new ChannelInfo { Name = name, Attribs = channelAttribute };
3895 #endif
3896 public void StartEvent(string eventName, EventAttribute eventAttribute)
3898 Contract.Assert(numParams == 0);
3899 Contract.Assert(templateName == null);
3900 templateName = eventName + "Args";
3901 numParams = 0;
3903 events.Append(" <event").
3904 Append(" value=\"").Append(eventAttribute.EventId).Append("\"").
3905 Append(" version=\"").Append(eventAttribute.Version).Append("\"").
3906 Append(" level=\"").Append(GetLevelName(eventAttribute.Level)).Append("\"");
3908 WriteMessageAttrib(events, "event", eventName, eventAttribute.Message);
3910 if (eventAttribute.Keywords != 0)
3911 events.Append(" keywords=\"").Append(GetKeywords((ulong)eventAttribute.Keywords, eventName)).Append("\"");
3912 if (eventAttribute.Opcode != 0)
3913 events.Append(" opcode=\"").Append(GetOpcodeName(eventAttribute.Opcode, eventName)).Append("\"");
3914 if (eventAttribute.Task != 0)
3915 events.Append(" task=\"").Append(GetTaskName(eventAttribute.Task, eventName)).Append("\"");
3916 #if FEATURE_MANAGED_ETW_CHANNELS
3917 if (eventAttribute.Channel != 0)
3918 events.Append(" channel=\"").Append(GetChannelName(eventAttribute.Channel, eventName)).Append("\"");
3919 #endif
3922 public void AddEventParameter(Type type, string name)
3924 if (numParams == 0)
3925 templates.Append(" <template tid=\"").Append(templateName).Append("\">").AppendLine();
3926 numParams++;
3927 templates.Append(" <data name=\"").Append(name).Append("\" inType=\"").Append(GetTypeName(type)).Append("\"");
3928 if (type.IsEnum)
3930 templates.Append(" map=\"").Append(type.Name).Append("\"");
3931 if (mapsTab == null)
3932 mapsTab = new Dictionary<string, Type>();
3933 if (!mapsTab.ContainsKey(type.Name))
3934 mapsTab.Add(type.Name, type); // Remember that we need to dump the type enumeration
3937 templates.Append("/>").AppendLine();
3939 public void EndEvent()
3941 if (numParams > 0)
3943 templates.Append(" </template>").AppendLine();
3944 events.Append(" template=\"").Append(templateName).Append("\"");
3946 events.Append("/>").AppendLine();
3948 templateName = null;
3949 numParams = 0;
3952 public byte[] CreateManifest()
3954 string str = CreateManifestString();
3955 return Encoding.UTF8.GetBytes(str);
3957 private string CreateManifestString()
3960 #if FEATURE_MANAGED_ETW_CHANNELS
3961 // Write out the channels
3962 if (channelTab != null)
3964 sb.Append(" <channels>").AppendLine();
3965 var sortedChannels = new List<int>(channelTab.Keys);
3966 sortedChannels.Sort();
3967 foreach (int channel in sortedChannels)
3969 var channelInfo = channelTab[channel];
3972 string channelType = null;
3973 string elementName = "channel";
3974 bool enabled = false;
3975 string isolation = null;
3976 string fullName = null;
3977 if (channelInfo.Attribs != null)
3979 var attribs = channelInfo.Attribs;
3980 channelType = attribs.Type;
3981 if (attribs.ImportChannel != null)
3983 fullName = attribs.ImportChannel;
3984 elementName = "importChannel";
3986 enabled = attribs.Enabled;
3987 isolation = attribs.Isolation;
3989 if (fullName == null)
3990 fullName = providerName + "/" + channelType;
3993 sb.Append(" <").Append(elementName);
3994 sb.Append(" chid=\"").Append(channelInfo.Name).Append("\"");
3995 sb.Append(" name=\"").Append(fullName).Append("\"");
3996 sb.Append(" value=\"").Append(channel).Append("\"");
3997 if (elementName == "channel") // not applicable to importChannels.
3999 if (channelType != null)
4000 sb.Append(" type=\"").Append(channelType).Append("\"");
4001 sb.Append(" enabled=\"").Append(enabled.ToString()).Append("\"");
4002 if (isolation != null)
4003 sb.Append(" isolation=\"").Append(isolation).Append("\"");
4005 sb.Append("/>").AppendLine();
4007 sb.Append(" </channels>").AppendLine();
4009 #endif
4011 // Write out the tasks
4012 if (taskTab != null)
4015 sb.Append(" <tasks>").AppendLine();
4016 var sortedTasks = new List<int>(taskTab.Keys);
4017 sortedTasks.Sort();
4018 foreach (int task in sortedTasks)
4020 sb.Append(" <task name=\"").Append(taskTab[task]).
4021 Append("\" value=\"").Append(task).
4022 Append("\"/>").AppendLine();
4024 sb.Append(" </tasks>").AppendLine();
4027 // Write out the maps
4028 if (mapsTab != null)
4030 sb.Append(" <maps>").AppendLine();
4031 foreach (Type enumType in mapsTab.Values)
4033 string mapKind = EventSource.GetCustomAttributeHelper(enumType, typeof(FlagsAttribute)) != null ? "bitMap" : "valueMap";
4034 sb.Append(" <").Append(mapKind).Append(" name=\"").Append(enumType.Name).Append("\">").AppendLine();
4036 // write out each enum value
4037 FieldInfo[] staticFields = enumType.GetFields(BindingFlags.DeclaredOnly | BindingFlags.Public | BindingFlags.Static);
4038 foreach (FieldInfo staticField in staticFields)
4040 object constantValObj = staticField.GetRawConstantValue();
4041 if (constantValObj != null)
4043 string hexStr = null;
4044 if (constantValObj is int)
4045 hexStr = ((int)constantValObj).ToString("x", CultureInfo.InvariantCulture);
4046 else if (constantValObj is long)
4047 hexStr = ((long)constantValObj).ToString("x", CultureInfo.InvariantCulture);
4048 sb.Append(" <map value=\"0x").Append(hexStr).Append("\"");
4049 WriteMessageAttrib(sb, "map", enumType.Name + "." + staticField.Name, staticField.Name);
4050 sb.Append("/>").AppendLine();
4053 sb.Append(" </").Append(mapKind).Append(">").AppendLine();
4055 sb.Append(" </maps>").AppendLine();
4058 // Write out the opcodes
4059 sb.Append(" <opcodes>").AppendLine();
4060 var sortedOpcodes = new List<int>(opcodeTab.Keys);
4061 sortedOpcodes.Sort();
4062 foreach (int opcode in sortedOpcodes)
4064 sb.Append(" <opcode");
4065 WriteNameAndMessageAttribs(sb, "opcode", opcodeTab[opcode]);
4066 sb.Append(" value=\"").Append(opcode).Append("\"/>").AppendLine();
4068 sb.Append(" </opcodes>").AppendLine();
4070 // Write out the keywords
4071 if (keywordTab != null)
4073 sb.Append(" <keywords>").AppendLine();
4074 var sortedKeywords = new List<ulong>(keywordTab.Keys);
4075 sortedKeywords.Sort();
4076 foreach (ulong keyword in sortedKeywords)
4078 sb.Append(" <keyword");
4079 WriteNameAndMessageAttribs(sb, "keyword", keywordTab[keyword]);
4080 sb.Append(" mask=\"0x").Append(keyword.ToString("x", CultureInfo.InvariantCulture)).Append("\"/>").AppendLine();
4082 sb.Append(" </keywords>").AppendLine();
4085 sb.Append(" <events>").AppendLine();
4086 sb.Append(events);
4087 sb.Append(" </events>").AppendLine();
4089 if (templates.Length > 0)
4091 sb.Append(" <templates>").AppendLine();
4092 sb.Append(templates);
4093 sb.Append(" </templates>").AppendLine();
4095 sb.Append("</provider>").AppendLine();
4096 sb.Append("</events>").AppendLine();
4097 sb.Append("</instrumentation>").AppendLine();
4099 // Output the localization information.
4100 sb.Append("<localization>").AppendLine();
4103 sb.Append(" <resources culture=\"").Append(CultureInfo.CurrentUICulture.Name).Append("\">").AppendLine();
4104 sb.Append(" <stringTable>").AppendLine();
4106 var sortedStrings = new string[stringTab.Keys.Count];
4107 stringTab.Keys.CopyTo(sortedStrings, 0);
4108 // Avoid using public Array.Sort as that attempts to access BinaryCompatibility. Unfortunately FrameworkEventSource gets called
4109 // very early in the app domain creation, when _FusionStore is not set up yet, resulting in a failure to run the static constructory
4110 // for BinaryCompatibility. This failure is then cached and a TypeInitializationException is thrown every time some code attampts to
4111 // access BinaryCompatibility.
4112 ArraySortHelper<string>.IntrospectiveSort(sortedStrings, 0, sortedStrings.Length, Comparer<string>.Default);
4113 foreach (var stringKey in sortedStrings)
4114 sb.Append(" <string id=\"").Append(stringKey).Append("\" value=\"").Append(stringTab[stringKey]).Append("\"/>").AppendLine();
4115 sb.Append(" </stringTable>").AppendLine();
4116 sb.Append(" </resources>").AppendLine();
4117 sb.Append("</localization>").AppendLine();
4118 sb.AppendLine("</instrumentationManifest>");
4119 return sb.ToString();
4122 #region private
4123 private void WriteNameAndMessageAttribs(StringBuilder stringBuilder, string elementName, string name)
4125 stringBuilder.Append(" name=\"").Append(name).Append("\" ");
4126 WriteMessageAttrib(sb, elementName, name, name);
4128 private void WriteMessageAttrib(StringBuilder stringBuilder, string elementName, string name, string value)
4130 string key = elementName + "_" + name;
4131 // See if the user wants things localized.
4132 if (resources != null)
4134 string localizedString = resources.GetString(key);
4135 if (localizedString != null)
4136 value = localizedString;
4138 if (value == null)
4139 return;
4140 if (elementName == "event")
4141 value = TranslateToManifestConvention(value);
4143 stringBuilder.Append(" message=\"$(string.").Append(key).Append(")\"");
4144 stringTab.Add(key, value);
4147 private static string GetLevelName(EventLevel level)
4149 return (((int)level >= 16) ? "" : "win:") + level.ToString();
4152 #if FEATURE_MANAGED_ETW_CHANNELS
4153 private string GetChannelName(EventChannel channel, string eventName)
4155 ChannelInfo info = null;
4156 if (channelTab == null || !channelTab.TryGetValue((int)channel, out info))
4157 throw new ArgumentException(Environment.GetResourceString("EventSource_UndefinedChannel", channel, eventName));
4158 return info.Name;
4160 #endif
4161 private string GetTaskName(EventTask task, string eventName)
4163 if (task == EventTask.None)
4164 return "";
4166 string ret;
4167 if (taskTab == null)
4168 taskTab = new Dictionary<int, string>();
4169 if (!taskTab.TryGetValue((int)task, out ret))
4170 ret = taskTab[(int)task] = eventName;
4171 return ret;
4173 private string GetOpcodeName(EventOpcode opcode, string eventName)
4175 switch (opcode)
4177 case EventOpcode.Info:
4178 return "win:Info";
4179 case EventOpcode.Start:
4180 return "win:Start";
4181 case EventOpcode.Stop:
4182 return "win:Stop";
4183 case EventOpcode.DataCollectionStart:
4184 return "win:DC_Start";
4185 case EventOpcode.DataCollectionStop:
4186 return "win:DC_Stop";
4187 case EventOpcode.Extension:
4188 return "win:Extension";
4189 case EventOpcode.Reply:
4190 return "win:Reply";
4191 case EventOpcode.Resume:
4192 return "win:Resume";
4193 case EventOpcode.Suspend:
4194 return "win:Suspend";
4195 case EventOpcode.Send:
4196 return "win:Send";
4197 case EventOpcode.Receive:
4198 return "win:Receive";
4201 string ret;
4202 if (opcodeTab == null || !opcodeTab.TryGetValue((int)opcode, out ret))
4203 throw new ArgumentException(Environment.GetResourceString("EventSource_UndefinedOpcode", opcode, eventName));
4204 return ret;
4206 private string GetKeywords(ulong keywords, string eventName)
4208 string ret = "";
4209 for (ulong bit = 1; bit != 0; bit <<= 1)
4211 if ((keywords & bit) != 0)
4213 string keyword;
4214 if (keywordTab == null || !keywordTab.TryGetValue(bit, out keyword))
4215 throw new ArgumentException(Environment.GetResourceString("EventSource_UndefinedKeyword", bit.ToString("x", CultureInfo.CurrentCulture), eventName));
4216 if (ret.Length != 0)
4217 ret = ret + " ";
4218 ret = ret + keyword;
4221 return ret;
4223 private static string GetTypeName(Type type)
4225 if (type.IsEnum)
4227 FieldInfo[] fields = type.GetFields(BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance);
4228 var typeName = GetTypeName(fields[0].FieldType);
4229 return typeName.Replace("win:Int", "win:UInt"); // ETW requires enums to be unsigned.
4231 switch (Type.GetTypeCode(type))
4233 case TypeCode.Boolean:
4234 return "win:Boolean";
4235 case TypeCode.Byte:
4236 return "win:UInt8";
4237 case TypeCode.UInt16:
4238 return "win:UInt16";
4239 case TypeCode.UInt32:
4240 return "win:UInt32";
4241 case TypeCode.UInt64:
4242 return "win:UInt64";
4243 case TypeCode.SByte:
4244 return "win:Int8";
4245 case TypeCode.Int16:
4246 return "win:Int16";
4247 case TypeCode.Int32:
4248 return "win:Int32";
4249 case TypeCode.Int64:
4250 return "win:Int64";
4251 case TypeCode.String:
4252 return "win:UnicodeString";
4253 case TypeCode.Single:
4254 return "win:Float";
4255 case TypeCode.Double:
4256 return "win:Double";
4257 case TypeCode.DateTime:
4258 return "win:FILETIME";
4259 default:
4260 if (type == typeof(Guid))
4261 return "win:GUID";
4262 throw new ArgumentException(Environment.GetResourceString("EventSource_UnsupportedEventTypeInManifest", type.Name));
4266 // Manifest messages use %N conventions for their message substitutions. Translate from
4267 // .NET conventions. We can't use RegEx for this (we are in mscorlib), so we do it 'by hand'
4268 private static string TranslateToManifestConvention(string eventMessage)
4270 StringBuilder stringBuilder = null; // We lazily create this
4271 int writtenSoFar = 0;
4272 int chIdx = -1;
4273 for (int i = 0; ; )
4275 if (i >= eventMessage.Length)
4277 if (stringBuilder == null)
4278 return eventMessage;
4279 stringBuilder.Append(eventMessage, writtenSoFar, i - writtenSoFar);
4280 return stringBuilder.ToString();
4282 if (eventMessage[i] == '{')
4284 int leftBracket = i;
4285 i++;
4286 int argNum = 0;
4287 while (i < eventMessage.Length && Char.IsDigit(eventMessage[i]))
4289 argNum = argNum * 10 + eventMessage[i] - '0';
4290 i++;
4292 if (i < eventMessage.Length && eventMessage[i] == '}')
4294 i++;
4295 if (stringBuilder == null)
4296 stringBuilder = new StringBuilder();
4297 stringBuilder.Append(eventMessage, writtenSoFar, leftBracket - writtenSoFar);
4298 stringBuilder.Append('%').Append(argNum + 1);
4299 writtenSoFar = i;
4302 else if ((chIdx = "&<>'\"".IndexOf(eventMessage[i])) >= 0)
4304 string[] xmlEscapes = {"&amp;", "&lt;", "&gt;", "&apos;", "&quot;"};
4305 var update = new Action<char, string>(
4306 (ch, escape) => {
4307 if (stringBuilder == null)
4308 stringBuilder = new StringBuilder();
4309 stringBuilder.Append(eventMessage, writtenSoFar, i - writtenSoFar);
4310 i++;
4311 stringBuilder.Append(escape);
4312 writtenSoFar = i;
4314 update(eventMessage[i], xmlEscapes[chIdx]);
4316 else
4317 i++;
4321 #if FEATURE_MANAGED_ETW_CHANNELS
4322 class ChannelInfo
4324 public string Name;
4325 public ChannelAttribute Attribs;
4327 #endif
4329 Dictionary<int, string> opcodeTab;
4330 Dictionary<int, string> taskTab;
4331 #if FEATURE_MANAGED_ETW_CHANNELS
4332 Dictionary<int, ChannelInfo> channelTab;
4333 #endif
4334 Dictionary<ulong, string> keywordTab;
4335 Dictionary<string, Type> mapsTab;
4337 Dictionary<string, string> stringTab; // Maps unlocalized strings to localized ones
4339 StringBuilder sb; // Holds the provider information.
4340 StringBuilder events; // Holds the events.
4341 StringBuilder templates;
4343 #if FEATURE_MANAGED_ETW_CHANNELS
4344 string providerName;
4345 #endif
4346 ResourceManager resources; // Look up localized strings here.
4348 // State we track between StartEvent and EndEvent.
4349 string templateName; // Every event gets its own template (eventName + "Args") this hold it.
4350 int numParams; // keeps track of the number of args the event has.
4351 #endregion
4354 /// <summary>
4355 /// Used to send the m_rawManifest into the event dispatcher as a series of events.
4356 /// </summary>
4357 internal struct ManifestEnvelope
4359 public const int MaxChunkSize = 0xFF00;
4360 public enum ManifestFormats : byte
4362 SimpleXmlFormat = 1, // simply dump the XML manifest as UTF8
4365 public ManifestFormats Format;
4366 public byte MajorVersion;
4367 public byte MinorVersion;
4368 public byte Magic;
4369 public ushort TotalChunks;
4370 public ushort ChunkNumber;
4373 #endregion