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