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
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
;
23 using System
.Threading
;
24 using Microsoft
.Win32
;
26 namespace System
.Diagnostics
.Tracing
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).
32 /// sealed class MinimalEventSource : EventSource
34 /// * public void Load(long ImageBase, string Name) { WriteEvent(1, ImageBase, Name); }
35 /// * public void Unload(long ImageBase) { WriteEvent(2, ImageBase); }
36 /// * private MinimalEventSource() {}
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.
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).
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
50 public class EventSource
: IDisposable
53 /// The human-friendly name of the eventSource. It defaults to the simple name of the class
55 public string Name { get { return m_name; }
}
57 /// Every eventSource is assigned a GUID to uniquely identify it to the system.
59 public Guid Guid { get { return m_guid; }
}
62 /// Returns true if the eventSource has been enabled at all.
64 [SuppressMessage("Microsoft.Concurrency", "CA8001", Justification
= "This does not need to be correct when racing with other threads")]
66 [System
.Runtime
.TargetedPatchingOptOut("Performance critical to inline across NGen image boundaries")]
68 public bool IsEnabled()
70 return m_eventSourceEnabled
;
73 /// Returns true if events with >= 'level' and have one of 'keywords' set are enabled.
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.
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
)
86 if (m_level
!= 0 && m_level
< level
)
88 if (m_matchAnyKeyword
!= 0 && (keywords
& m_matchAnyKeyword
) == 0)
91 #if !FEATURE_ACTIVITYSAMPLING
95 #else // FEATURE_ACTIVITYSAMPLING
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)
116 // if there's at least one legacy ETW listener we can't filter this
117 if (m_legacySessions
!= null && m_legacySessions
.Count
> 0)
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)
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)
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
)
141 else if (ActivityFilter
.IsCurrentActivityActive(activityFilter
))
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
])
157 else if (ActivityFilter
.IsCurrentActivityActive(activityFilter
))
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.
165 #endif // OPTIMIZE_IS_ENABLED
167 #endif // FEATURE_ACTIVITYSAMPLING
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.
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
;
186 if (attrib
.Guid
!= null)
189 if(Guid
.TryParse(attrib
.Guid
, out g
))
193 if (attrib
.Name
!= null)
198 throw new ArgumentException("eventSourceType", Environment
.GetResourceString("Argument_InvalidTypeName"));
200 return GenerateGuidFromName(name
.ToUpperInvariant()); // Make it case insensitive.
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.
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)
217 return eventSourceType
.Name
;
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
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
238 /// returns a list (IEnumerable) of all sources in the appdomain). EventListners typically need this.
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
);
257 /// Send a command to a particular EventSource identified by 'eventSource'
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.
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).
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)
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).
289 /// All events created with the EventSource on this thread are also tagged with the
290 /// activity ID of the thread.
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
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
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.
333 /// All events created with the EventSource on this thread are also tagged with the
334 /// activity ID of the thread.
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
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
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
,
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.
376 internal static Guid InternalCurrentThreadActivityId
378 [System
.Security
.SecurityCritical
]
381 Guid retval
= CurrentThreadActivityId
;
382 if (retval
== Guid
.Empty
)
384 retval
= FallbackActivityId
;
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)
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)
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).
415 public Exception ConstructionException { get { return m_constructionException; }
}
418 /// Displays thew name and GUID for the eventSoruce for debugging purposes.
420 public override string ToString() { return Environment.GetResourceString("EventSource_ToString", Name, Guid); }
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.
431 protected EventSource()
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.
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
;
456 Contract
.Assert(m_eventData
== null && m_eventSourceEnabled
== false);
457 ReportOutOfBandMessage("ERROR: Exception during construction of EventSource " + Name
+ ": "
459 m_eventSourceEnabled
= false; // This is insurance, it should still be off.
460 if (m_lastCommandException
!= null)
461 m_constructionException
= m_lastCommandException
;
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.
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
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
525 // public static EventSource Log;
527 // class MyEventListener: EventListener
529 // protected override void OnEventWritten(...)
530 // { MyEventSource.Log.anything; } <-- AV, as the static Log was not set yet
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
);
542 /// This method is called when the eventSource is updated by the controller.
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
);
571 List
<object> arg
= new List
<object>();
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
);
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
);
612 descrs
[1].DataPointer
= (IntPtr
)(&arg2
);
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
);
627 descrs
[1].DataPointer
= (IntPtr
)(&arg2
);
629 descrs
[2].DataPointer
= (IntPtr
)(&arg3
);
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
);
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
);
658 descrs
[1].DataPointer
= (IntPtr
)(&arg2
);
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
);
673 descrs
[1].DataPointer
= (IntPtr
)(&arg2
);
675 descrs
[2].DataPointer
= (IntPtr
)(&arg3
);
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
);
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
);
779 descrs
[2].DataPointer
= (IntPtr
)(&arg3
);
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
);
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; }
}
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.
816 internal int m_Reserved
; // Used to pad the size to match the Win32 API
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.
825 [CLSCompliant(false)]
826 protected unsafe void WriteEventCore(int eventId
, int eventDataCount
, EventSource
.EventData
* data
)
828 WriteEventWithRelatedActivityIdCore(eventId
, null, eventDataCount
, data
);
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
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();
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();
892 if (!m_provider
.WriteEvent(ref m_eventData
[eventId
].Descriptor
, childActivityID
, eventDataCount
, (IntPtr
)data
))
893 ThrowEventSourceException();
894 #endif // FEATURE_ACTIVITYSAMPLING
897 if (m_Dispatchers
!= null && m_eventData
[eventId
].EnabledForAnyListener
)
898 WriteToAllListeners(eventId
, childActivityID
, eventDataCount
, data
);
902 // fallback varags helpers.
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.
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
);
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.
925 [SecuritySafeCritical
]
926 protected unsafe void WriteEventWithRelatedActivityId(int eventId
, Guid childActivityID
, params object[] args
)
928 WriteEventVarargs(eventId
, &childActivityID
, args
);
933 #region IDisposable Members
934 public void Dispose()
937 GC
.SuppressFinalize(this);
940 /// Disposes of an EventSource.
943 /// Called from Dispose() with disposing=true, and from the finalizer (~MeasurementBlock) with disposing=false.
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.
948 /// <param name="disposing">True if called from Dispose(), false if called from the finalizer.</param>
949 protected virtual void Dispose(bool disposing
)
953 #if FEATURE_MANAGED_ETW
954 if (m_provider
!= null)
956 m_provider
.Dispose();
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
;
984 bytes
[i
+ 4] = (byte)namespace2
;
986 bytes
[i
+ 8] = (byte)namespace3
;
988 bytes
[i
+ 12] = (byte)namespace4
;
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]);
1013 private unsafe object DecodeObject(int eventId
, int parameterId
, IntPtr dataPointer
)
1015 Type dataType
= m_eventData
[eventId
].Parameters
[parameterId
].ParameterType
;
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)
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
);
1093 if (dataType
.IsEnum
)
1095 dataType
= Enum
.GetUnderlyingType(dataType
);
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
1108 private EventDispatcher
GetDispatcher(EventListener listener
)
1110 EventDispatcher dispatcher
= m_Dispatchers
;
1111 while (dispatcher
!= null)
1113 if (dispatcher
.m_Listener
== listener
)
1115 dispatcher
= dispatcher
.m_Next
;
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();
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();
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
);
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.
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
1209 if (activityFilter
== null ||
1210 ActivityFilter
.PassesActivityFilter(activityFilter
, childActivityID
,
1211 m_eventData
[eventId
].TriggersActivityTracking
> 0,
1213 !dispatcher
.m_activityFilteringEnabled
)
1214 #endif // FEATURE_ACTIVITYSAMPLING
1218 dispatcher
.m_Listener
.OnEventWritten(eventCallbackArgs
);
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;
1271 if (dispatcherEnabled
)
1272 dispatcher
.m_Listener
.OnEventWritten(eventCallbackArgs
);
1276 lastThrownException
= e
;
1279 if (lastThrownException
!= null)
1281 throw new EventSourceException(lastThrownException
);
1285 #if FEATURE_ACTIVITYSAMPLING
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)
1325 ActivityFilter activityFilter
= legacyEtwSession
.m_activityFilter
;
1326 if (activityFilter
!= null)
1328 if (pCurrentActivityId
== null)
1330 currentActivityId
= InternalCurrentThreadActivityId
;
1331 pCurrentActivityId
= ¤tActivityId
;
1333 ActivityFilter
.FlowActivityIfNeeded(activityFilter
, pCurrentActivityId
, childActivityID
);
1340 #endif // FEATURE_ACTIVITYSAMPLING
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.
1346 private bool IsEnabledByDefault(int eventNum
, bool enable
, EventLevel currentLevel
, EventKeywords currentMatchAnyKeyword
)
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))
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"));
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
1401 /// This class lets us hook the 'OnEventCommand' from the eventSource.
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
;
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.
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
));
1493 if (!m_eventSourceEnabled
)
1495 // EventSource turned on for the first time, simply copy the bits.
1497 m_matchAnyKeyword
= matchAnyKeyword
;
1501 // Already enabled, make it the most verbose of the existing and new filter
1502 if (level
> m_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
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
);
1551 m_deferedSendManifest
= true;
1554 #if FEATURE_ACTIVITYSAMPLING
1555 if (bSessionEnable
&& perEventSourceSessionId
!= -1)
1557 bool participateInSampling
= false;
1558 string activityFilters
;
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
);
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
1603 m_eventSourceEnabled
= true;
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();
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;
1651 m_eventData
[i
].EnabledForAnyListener
= isEnabledForAnyListener
;
1654 // If no events are enabled, disable the global enabled bit.
1655 if (!AnyEventEnabled())
1658 m_matchAnyKeyword
= 0;
1659 m_eventSourceEnabled
= false;
1662 #if FEATURE_ACTIVITYSAMPLING
1663 UpdateKwdTriggers(enable
);
1664 #endif // FEATURE_ACTIVITYSAMPLING
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
1693 // Remember any exception and rethrow.
1694 m_lastCommandException
= e
;
1699 #if FEATURE_ACTIVITYSAMPLING
1701 internal void UpdateEtwSession(
1705 string activityFilters
,
1706 bool participateInSampling
)
1708 if (sessionIdBit
< SessionMask
.MAX
)
1710 // activity-tracing-aware etw session
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
;
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
;
1731 // legacy etw session
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
);
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
)
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;
1772 participateInSampling
= true;
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
)
1787 sessionIdBit
= sessionKwd
- SessionMask
.SHIFT_SESSION_TO_KEYWORD
;
1792 internal void UpdateKwdTriggers(bool enable
)
1796 // recompute m_keywordTriggers
1797 ulong gKeywords
= (ulong)m_matchAnyKeyword
;
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)
1808 ActivityFilter activityFilter
= etwSession
.m_activityFilter
;
1809 ActivityFilter
.UpdateKwdTriggers(activityFilter
, m_guid
, this, (EventKeywords
)gKeywords
);
1814 m_keywordTriggers
= 0;
1818 #endif // FEATURE_ACTIVITYSAMPLING
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.
1825 internal bool EnableEventForDispatcher(EventDispatcher dispatcher
, int eventId
, bool value)
1827 if (dispatcher
== null)
1829 if (eventId
>= m_eventData
.Length
)
1831 #if FEATURE_MANAGED_ETW
1832 if (m_provider
!= null)
1833 m_eventData
[eventId
].EnabledForETW
= value;
1838 if (eventId
>= dispatcher
.m_EventEnabled
.Length
)
1840 dispatcher
.m_EventEnabled
[eventId
] = value;
1842 m_eventData
[eventId
].EnabledForAnyListener
= true;
1848 /// Returns true if any event at all is on.
1850 private bool AnyEventEnabled()
1852 for (int i
= 0; i
< m_eventData
.Length
; i
++)
1853 if (m_eventData
[i
].EnabledForETW
|| m_eventData
[i
].EnabledForAnyListener
)
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
;
1942 if(m_throwOnEventWriteErrors
)
1943 ThrowEventSourceException();
1946 dataLeft
-= ManifestEnvelope
.MaxChunkSize
;
1947 dataDescrs
[1].Ptr
+= ManifestEnvelope
.MaxChunkSize
;
1948 envelope
.ChunkNumber
++;
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");
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");
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
);
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);
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;
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" }
)
2047 foreach (var providerEnumKind
in new string[] { "Keywords", "Tasks", "Opcodes" }
)
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
));
2075 if (method
.IsVirtual
|| method
.IsStatic
)
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)
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"));
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)
2106 manifest
.AddEventParameter(args
[fieldIdx
].ParameterType
, args
[fieldIdx
].Name
);
2108 manifest
.EndEvent();
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
);
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'
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();
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
);
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
,
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
;
2202 if (eventData
[idx
].Descriptor
.EventId
!= 0)
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
;
2253 /// This method looks at the IL and tries to pattern match against the standard
2254 /// 'boilerplate' event body
2256 /// { if (Enabled()) WriteEvent(#, ...) }
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.
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
2275 // ... // CAN ONLY BE THE INSTRUCTIONS BELOW CAN'T BE A BRANCH OR A CALL
2277 // NOP // 0 or more times
2280 // If we find this pattern we return the XXX. Otherwise we return -1.
2281 byte[] instrs
= method
.GetMethodBody().GetILAsByteArray();
2283 for (int idx
= 0; idx
< instrs
.Length
; )
2285 switch (instrs
[idx
])
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;
2321 case 31: // LDC_I4_S
2322 if (idx
> 0 && instrs
[idx
- 1] == 2) // preceeded by LDARG0
2323 retVal
= instrs
[idx
+ 1];
2336 // Is this call just before return?
2337 for (int search
= idx
+ 1; search
< instrs
.Length
; search
++)
2339 if (instrs
[search
] == 42) // RET
2341 if (instrs
[search
] != 0) // NOP
2347 case 44: // BRFALSE_S
2348 case 45: // BRTRUE_S
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
2368 case 162: // STELEM_REF
2372 // Covers the CEQ instructions used in debug code for some reason.
2373 if (idx
>= instrs
.Length
|| instrs
[idx
] >= 6)
2377 /* Contract.Assert(false, "Warning: User validation code sub-optimial: Unsuported opcode " + instrs[idx] +
2378 " at " + idx + " in method " + method.Name); */
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
;
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
;
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
])
2434 if (listener
== null)
2436 EtwSession etwSession
= m_etwSessionIdMap
[perEventSourceSessionId
];
2437 Contract
.Assert(etwSession
!= null);
2438 af
= etwSession
.m_activityFilter
;
2442 af
= listener
.m_activityFilter
;
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
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
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
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
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.
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.
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.
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.
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.
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
2541 public abstract class EventListener
: IDisposable
2543 private static bool s_CreatingListener
= false;
2545 /// Create a new EventListener in which all events start off truned off (use EnableEvents to turn
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
;
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
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.
2584 s_CreatingListener
= false;
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.
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
);
2609 // Find 'this' from the s_Listeners linked list.
2610 EventListener prev
= s_Listeners
;
2613 EventListener cur
= prev
.m_Next
;
2618 // Found our Listener, remove references to to it in the eventSources
2619 prev
.m_Next
= cur
.m_Next
; // Remove entry.
2620 RemoveReferencesToListenerInEventSources(cur
);
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.
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.
2639 /// This call never has an effect on other EventListeners.
2641 /// Returns 'true' if any eventSource could be found that matches 'eventSourceGuid'
2643 public void EnableEvents(EventSource eventSource
, EventLevel level
)
2645 EnableEvents(eventSource
, level
, EventKeywords
.None
);
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'.
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.
2656 /// If eventSourceGuid is Guid.Empty, then the affects all eventSources in the appdomain
2658 /// If eventSourceGuid is not Guid.Empty, this call has no effect on any other eventSources in the appdomain.
2660 /// This call never has an effect on other EventListeners.
2662 /// Returns 'true' if any eventSource could be found that matches 'eventSourceGuid'
2664 public void EnableEvents(EventSource eventSource
, EventLevel level
, EventKeywords matchAnyKeyword
)
2666 EnableEvents(eventSource
, level
, matchAnyKeyword
, null);
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.
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.
2678 /// This call never has an effect on other EventListeners.
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
);
2691 /// Disables all events coming from eventSource identified by 'eventSource'.
2693 /// If eventSourceGuid is Guid.Empty, then the affects all eventSources in the appdomain
2695 /// This call never has an effect on other EventListeners.
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);
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.
2714 /// These 'catch up' events are called during the construction of the EventListener.
2715 /// Subclasses need to be prepared for that.
2717 /// In a multi-threaded environment, it is possible that 'OnEventWritten' callbacks
2718 /// for a paritcular eventSource to occur BEFORE the OnEventSourceCreated is issued.
2720 /// <param name="eventSource"></param>
2721 internal protected virtual void OnEventSourceCreated(EventSource eventSource
) { }
2723 /// This method is called whenever an event has been written by a EventSource for which the EventListener
2724 /// has enabled events.
2726 internal protected abstract void OnEventWritten(EventWrittenEventArgs eventData
);
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)).
2734 static protected int EventSourceIndex(EventSource eventSource
) { return eventSource.m_id; }
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).
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).
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).
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.
2765 WeakReference weakRef
= s_EventSources
[i
];
2766 if (!weakRef
.IsAlive
)
2769 weakRef
.Target
= newEventSource
;
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
);
2790 /// Helper used in code:Dispose that removes any references to 'listenerToRemove' in any of the
2791 /// eventSources in the appdomain.
2793 /// The EventListenersLock must be held before calling this routine.
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
;
2808 // Remove 'listenerToRemove' from the eventSource.m_Dispatchers linked list.
2809 EventDispatcher prev
= eventSource
.m_Dispatchers
;
2812 EventDispatcher cur
= prev
.m_Next
;
2815 Contract
.Assert(false, "EventSource did not have a registered EventListener!");
2818 if (cur
.m_Listener
== listenerToRemove
)
2820 prev
.m_Next
= cur
.m_Next
; // Remove entry.
2831 /// Checks internal consistancy of EventSources/Listeners.
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
;
2843 allListeners
.Add(cur
, true);
2847 // For all eventSources
2849 foreach (WeakReference eventSourceRef
in s_EventSources
)
2852 EventSource eventSource
= eventSourceRef
.Target
as EventSource
;
2853 if (eventSource
== null)
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
;
2871 Contract
.Assert(dispatcher
!= null, "Listener is not on all eventSources.");
2872 if (dispatcher
.m_Listener
== listener
)
2874 dispatcher
= dispatcher
.m_Next
;
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)
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
;
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
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
2909 /// Passed to the code:EventSource.OnEventCommand callback
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);
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
;
2946 /// code:EventWrittenEventArgs is passed when the callback given in code:EventListener.OnEventWritten is
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
]
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
2972 if (m_message
!= null)
2975 return m_eventSource
.m_eventData
[EventId
].Message
;
2983 #if FEATURE_MANAGED_ETW_CHANNELS
2984 public EventChannel Channel { get { return (EventChannel) m_eventSource.m_eventData[EventId].Descriptor.Channel; }}
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
;
2998 internal EventWrittenEventArgs(EventSource eventSource
)
3000 m_eventSource
= eventSource
;
3002 private string m_message
;
3003 private EventSource m_eventSource
;
3007 [AttributeUsage(AttributeTargets
.Class
)]
3008 public sealed class EventSourceAttribute
: Attribute
3010 public string Name { get; set; }
3011 public string Guid { get; set; }
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.
3020 /// LocalizationResources property is non-null, then EventSource will look up the localized strings for events by
3021 /// using the following resource naming scheme
3025 /// keyword_KEYWORDNAME
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.
3032 public string LocalizationResources { get; set; }
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
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; }
3054 public byte Version { get; set; }
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
3065 public string Message { get; set; }
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
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; }
3088 /// Legal values are in ChannelTypes
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";
3109 public enum EventCommand
3118 #region private classes
3120 #if FEATURE_ACTIVITYSAMPLING
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.
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.
3132 internal sealed class ActivityFilter
: IDisposable
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'.
3138 public static void DisableFilter(ref ActivityFilter filterList
, EventSource source
)
3140 Contract
.Assert(Monitor
.IsEntered(EventListener
.EventListenersLock
));
3142 if (filterList
== null)
3146 // Remove it from anywhere in the list (except the first element, which has to
3147 // be treated specially)
3148 ActivityFilter prev
= filterList
;
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
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
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
);
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'.
3200 public static void UpdateFilter(
3201 ref ActivityFilter filterList
,
3203 int perEventSourceSessionId
,
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
];
3226 int colonIdx
= activityFilterString
.IndexOf(':');
3229 source
.ReportOutOfBandMessage("ERROR: Invalid ActivitySamplingStartEvent specification: " +
3230 activityFilterString
, false);
3231 // ignore failure...
3234 string sFreq
= activityFilterString
.Substring(colonIdx
+ 1);
3235 if (!int.TryParse(sFreq
, out sampleFreq
))
3237 source
.ReportOutOfBandMessage("ERROR: Invalid sampling frequency specification: " + sFreq
, false);
3240 activityFilterString
= activityFilterString
.Substring(0, colonIdx
);
3241 if (!int.TryParse(activityFilterString
, out eventId
))
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
;
3257 if (eventId
< 0 || eventId
>= source
.m_eventData
.Length
)
3259 source
.ReportOutOfBandMessage("ERROR: Invalid eventId specification: " + activityFilterString
, false);
3262 EnableFilter(ref filterList
, source
, perEventSourceSessionId
, eventId
, sampleFreq
);
3268 /// Returns the first ActivityFilter from 'filterList' corresponding to 'source'.
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)
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.
3289 /// If 'childActivityID' is present, it will be added to the active set if the
3290 /// current activity is active.
3293 unsafe public static bool PassesActivityFilter(
3294 ActivityFilter filterList
,
3295 Guid
* childActivityID
,
3296 bool triggeringEvent
,
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
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
);
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)
3345 af
.m_activeActivities
.TryRemove(currentActivityId
, out dummy
);
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
))
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.
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
))
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
;
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
);
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.
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
);
3444 /// The cleanup being performed consists of removing the m_myActivityDelegate from
3445 /// the static s_activityDying, therefore allowing the ActivityFilter to be reclaimed.
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;
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
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
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
);
3494 m_activeActivities
= activeActivities
;
3495 m_rootActiveActivities
= existingFilter
.m_rootActiveActivities
;
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'.
3504 private static void EnsureActivityCleanupDelegate(ActivityFilter filterList
)
3506 if (filterList
== null)
3509 for (ActivityFilter af
= filterList
; af
!= null; af
= af
.m_next
)
3511 if (af
.m_myActivityDelegate
!= null)
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
);
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.
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
) =>
3532 filterList
.m_activeActivities
.TryRemove(oldActivity
, out dummy
);
3533 Tuple
<Guid
, int> dummyTuple
;
3534 filterList
.m_rootActiveActivities
.TryRemove(oldActivity
, out dummyTuple
);
3539 /// Enables activity filtering for the listener associated with 'filterList', triggering on
3540 /// the event 'eventID' from 'source' with a sampling frequency of 'samplingFreq'
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).
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
3557 if (0 <= eventId
&& eventId
< source
.m_eventData
.Length
)
3558 ++source
.m_eventData
[eventId
].TriggersActivityTracking
;
3564 /// Normally this code never runs, it is here just to prevent run-away resource usage.
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
++)
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
;
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
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
;
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.
3636 internal class EtwSession
3638 public static EtwSession
GetEtwSession(int etwSessionId
, bool bCreateIfNeeded
= false)
3640 if (etwSessionId
< 0)
3643 EtwSession etwSession
;
3644 foreach(var wrEtwSession
in s_etwSessions
)
3646 if (wrEtwSession
.TryGetTarget(out etwSession
) && etwSession
.m_etwSessionId
== etwSessionId
)
3650 if (!bCreateIfNeeded
)
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
)
3665 public static void RemoveEtwSession(EtwSession etwSession
)
3667 Contract
.Assert(etwSession
!= null);
3668 if (s_etwSessions
== null)
3671 s_etwSessions
.RemoveAll((wrEtwSession
) =>
3674 return wrEtwSession
.TryGetTarget(out session
) &&
3675 (session
.m_etwSessionId
== etwSession
.m_etwSessionId
);
3678 if (s_etwSessions
.Count
> s_thrSessionCount
)
3682 private static void TrimGlobalList()
3684 if (s_etwSessions
== null)
3687 s_etwSessions
.RemoveAll((wrEtwSession
) =>
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
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.
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
]
3753 Contract
.Assert(perEventSourceSessionId
< MAX
);
3754 return (m_mask
& (1 << perEventSourceSessionId
)) != 0;
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))
3799 /// code:EventDispatchers are a simple 'helper' structure that holds the filtering state
3800 /// (m_EventEnabled) for a particular EventSource X EventListener tuple
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).
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.
3810 internal class EventDispatcher
3812 internal EventDispatcher(EventDispatcher next
, bool[] eventEnabled
, EventListener listener
)
3815 m_EventEnabled
= eventEnabled
;
3816 m_Listener
= listener
;
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.
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.
3835 internal class ManifestBuilder
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.
3841 public ManifestBuilder(string providerName
, Guid providerGuid
, string dllName
, ResourceManager resources
)
3843 #if FEATURE_MANAGED_ETW_CHANNELS
3844 this.providerName
= providerName
;
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
3887 /// Add a channel. channelAttribute can be null
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 }
;
3896 public void StartEvent(string eventName
, EventAttribute eventAttribute
)
3898 Contract
.Assert(numParams
== 0);
3899 Contract
.Assert(templateName
== null);
3900 templateName
= eventName
+ "Args";
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("\"");
3922 public void AddEventParameter(Type type
, string name
)
3925 templates
.Append(" <template tid=\"").Append(templateName
).Append("\">").AppendLine();
3927 templates
.Append(" <data name=\"").Append(name
).Append("\" inType=\"").Append(GetTypeName(type
)).Append("\"");
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()
3943 templates
.Append(" </template>").AppendLine();
3944 events
.Append(" template=\"").Append(templateName
).Append("\"");
3946 events
.Append("/>").AppendLine();
3948 templateName
= null;
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();
4011 // Write out the tasks
4012 if (taskTab
!= null)
4015 sb
.Append(" <tasks>").AppendLine();
4016 var sortedTasks
= new List
<int>(taskTab
.Keys
);
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();
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();
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
;
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
));
4161 private string GetTaskName(EventTask task
, string eventName
)
4163 if (task
== EventTask
.None
)
4167 if (taskTab
== null)
4168 taskTab
= new Dictionary
<int, string>();
4169 if (!taskTab
.TryGetValue((int)task
, out ret
))
4170 ret
= taskTab
[(int)task
] = eventName
;
4173 private string GetOpcodeName(EventOpcode opcode
, string eventName
)
4177 case EventOpcode
.Info
:
4179 case EventOpcode
.Start
:
4181 case EventOpcode
.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
:
4191 case EventOpcode
.Resume
:
4192 return "win:Resume";
4193 case EventOpcode
.Suspend
:
4194 return "win:Suspend";
4195 case EventOpcode
.Send
:
4197 case EventOpcode
.Receive
:
4198 return "win:Receive";
4202 if (opcodeTab
== null || !opcodeTab
.TryGetValue((int)opcode
, out ret
))
4203 throw new ArgumentException(Environment
.GetResourceString("EventSource_UndefinedOpcode", opcode
, eventName
));
4206 private string GetKeywords(ulong keywords
, string eventName
)
4209 for (ulong bit
= 1; bit
!= 0; bit
<<= 1)
4211 if ((keywords
& bit
) != 0)
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)
4218 ret
= ret
+ keyword
;
4223 private static string GetTypeName(Type type
)
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";
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
:
4245 case TypeCode
.Int16
:
4247 case TypeCode
.Int32
:
4249 case TypeCode
.Int64
:
4251 case TypeCode
.String
:
4252 return "win:UnicodeString";
4253 case TypeCode
.Single
:
4255 case TypeCode
.Double
:
4256 return "win:Double";
4257 case TypeCode
.DateTime
:
4258 return "win:FILETIME";
4260 if (type
== typeof(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;
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
;
4287 while (i
< eventMessage
.Length
&& Char
.IsDigit(eventMessage
[i
]))
4289 argNum
= argNum
* 10 + eventMessage
[i
] - '0';
4292 if (i
< eventMessage
.Length
&& eventMessage
[i
] == '}')
4295 if (stringBuilder
== null)
4296 stringBuilder
= new StringBuilder();
4297 stringBuilder
.Append(eventMessage
, writtenSoFar
, leftBracket
- writtenSoFar
);
4298 stringBuilder
.Append('%').Append(argNum
+ 1);
4302 else if ((chIdx
= "&<>'\"".IndexOf(eventMessage
[i
])) >= 0)
4304 string[] xmlEscapes
= {"&", "<", ">", "'", """}
;
4305 var update
= new Action
<char, string>(
4307 if (stringBuilder
== null)
4308 stringBuilder
= new StringBuilder();
4309 stringBuilder
.Append(eventMessage
, writtenSoFar
, i
- writtenSoFar
);
4311 stringBuilder
.Append(escape
);
4314 update(eventMessage
[i
], xmlEscapes
[chIdx
]);
4321 #if FEATURE_MANAGED_ETW_CHANNELS
4325 public ChannelAttribute Attribs
;
4329 Dictionary
<int, string> opcodeTab
;
4330 Dictionary
<int, string> taskTab
;
4331 #if FEATURE_MANAGED_ETW_CHANNELS
4332 Dictionary
<int, ChannelInfo
> channelTab
;
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
;
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.
4355 /// Used to send the m_rawManifest into the event dispatcher as a series of events.
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
;
4369 public ushort TotalChunks
;
4370 public ushort ChunkNumber
;