Code:
/ 4.0 / 4.0 / DEVDIV_TFS / Dev10 / Releases / RTMRel / ndp / clr / src / BCL / System / Diagnostics / Eventing / EventProviderBase.cs / 1305376 / EventProviderBase.cs
//------------------------------------------------------------------------------ //// Copyright (c) Microsoft Corporation. All rights reserved. // //[....] //----------------------------------------------------------------------------- // This program uses code hyperlinks available as part of the HyperAddin Visual Studio plug-in. // It is available from http://www.codeplex.com/hyperAddin using System; using System.Collections.Generic; using System.Globalization; using System.Text; using System.Diagnostics.Eventing; using System.Diagnostics; using System.Reflection; using System.Threading; using System.Diagnostics.Contracts; // Implementation of my EventProvider scheme namespace System.Diagnostics.Eventing { ////// This class is meant to be inherited by a user provider (which provides specific events and then /// calls code:EventProviderBase.WriteEvent to log them). /// /// sealed class MinimalProvider : EventProviderBase /// { /// * public void Load(long ImageBase, string Name) { WriteEvent(1, ImageBase, Name); } /// * public void Unload(long ImageBase) { WriteEvent(2, ImageBase); } /// * private MinimalProvider() : base(new Guid(0xc836fd3, 0xee92, 0x4301, 0xbc, 0x13, 0xd8, 0x94, 0x3e, 0xc, 0x1e, 0x77)) {} /// } /// /// This functionaity is sufficient for many users. When more control is needed over the ETW manifest /// that is created, that can be done by adding [Event] attributes on the methods. /// /// Finally for very advanced Providers, it is possible to intercept the commands being given to the /// provider and change what filtering is done (or cause actions to be performed by the provider (eg /// duming a data structure). /// /// The providers can be turned on with Window ETW controllers (eg logman), immediately. It is also /// possible to control and intercept the data stream programatically. We code:EventProviderDataStream for /// more. /// [System.Runtime.CompilerServices.FriendAccessAllowed] internal class EventProviderBase : IDisposable { ////// Most events should be fired by calling methods on a subclass of code:EventProviderBase but we /// provide this API in the base to provide this trivial functionality in the case of a single /// string. /// public void WriteMessage(string eventMessage) { WriteMessage(eventMessage, EventLevel.LogAlways, EventKeywords.None); } public void WriteMessage(string eventMessage, EventLevel level, EventKeywords keywords) { #if ETW_SUPPORTED if(m_provider != null) m_provider.WriteMessageEvent(eventMessage, (byte)level, (long)keywords); #endif WriteToAllStreams(0, eventMessage); } ////// The human-friendly name of the provider. It defaults to the simple name of the class /// public string Name { get { return m_name; } } ////// Every provider is assigned a GUID to uniquely identify it to the system. /// public Guid Guid { get { return m_guid; } } ////// Returns true if the provider has been enabled at all. /// public bool IsEnabled() { return m_providerEnabled; } public bool IsEnabled(EventLevel level, EventKeywords keywords) { if (!m_providerEnabled) return false; if (m_level != 0 && m_level < level) return false; return m_matchAnyKeyword == 0 || (keywords & m_matchAnyKeyword) != 0; } ////// Returns a string of the XML manifest associated with the provider. The scheme for this XML is /// documented at in EventManifest Schema http://msdn.microsoft.com/en-us/library/aa384043(VS.85).aspx /// /// The manifest XML fragment contains the string name of the DLL name in /// which it is embeded. This parameter spcifies what name will be used ///The XML data string public string ProviderManifestXmlFragment(string providerDllName) { byte[] providerBytes = m_rawManifest; if (providerBytes == null) providerBytes = CreateManifestAndDescriptors(providerDllName); return Encoding.UTF8.GetString(providerBytes); } #region protected [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1720:IdentifiersShouldNotContainTypeNames", MessageId = "guid")] protected EventProviderBase(Guid providerGuid) : this(providerGuid, null) { } [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1720:IdentifiersShouldNotContainTypeNames", MessageId = "guid")] [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2214:DoNotCallOverridableMethodsInConstructors", Justification = "The constructor call chains to DoDebugChecks() is safe.")] [System.Security.SecuritySafeCritical] protected EventProviderBase(Guid providerGuid, string providerName) { if (providerName == null) providerName = GetType().Name; m_name = providerName; m_guid = providerGuid; #if ETW_SUPPORTED m_provider = new OverideEventProvider(this); try { m_provider.Register(providerGuid); } catch (ArgumentException) { // Failed to register. Don't crash the app, just don't write events to ETW. m_provider = null; } #endif if (m_providerEnabled && !m_ETWManifestSent) { SendManifest(m_rawManifest, null); m_ETWManifestSent = true; } m_completelyInited = true; // Add the provider to the global (weak) list. This also sets m_id, which is the // index in the list. EventProviderDataStream.AddProvider(this); } ////// This method is called when the provider is updated by the controller. /// protected virtual void OnControllerCommand(EventProviderDataStream outputStream, ControllerCommand command, IDictionaryarguments) { } // optimized for common signatures (no args) protected unsafe void WriteEvent(int eventId) { Contract.Assert(m_eventData[eventId].Descriptor.EventId != 0); #if ETW_SUPPORTED if (m_provider != null && m_eventData[eventId].EnabledForETW) { m_provider.WriteEvent(ref m_eventData[eventId].Descriptor, 0, (IntPtr)0); if (m_eventData[eventId].CaptureStack) CaptureStack(); } #endif if (m_OutputStreams != null && m_eventData[eventId].EnabledForAnyStream) WriteToAllStreams(eventId); } // optimized for common signatures (ints) [Security.SecuritySafeCritical] protected unsafe void WriteEvent(int eventId, int value) { Contract.Assert(m_eventData[eventId].Descriptor.EventId != 0); #if ETW_SUPPORTED if (m_provider != null && m_eventData[eventId].EnabledForETW) { EventProvider.EventData* dataDescrs = stackalloc EventProvider.EventData[1]; dataDescrs[0].Ptr = (ulong)&value; dataDescrs[0].Size = 4; dataDescrs[0].Reserved = 0; m_provider.WriteEvent(ref m_eventData[eventId].Descriptor, 1, (IntPtr)dataDescrs); if (m_eventData[eventId].CaptureStack) CaptureStack(); } #endif if (m_OutputStreams != null && m_eventData[eventId].EnabledForAnyStream) WriteToAllStreams(eventId, value); } [Security.SecuritySafeCritical] protected unsafe void WriteEvent(int eventId, int value1, int value2) { Contract.Assert(m_eventData[eventId].Descriptor.EventId != 0); #if ETW_SUPPORTED if (m_provider != null && m_eventData[eventId].EnabledForETW) { EventProvider.EventData* dataDescrs = stackalloc EventProvider.EventData[2]; dataDescrs[0].Ptr = (ulong)&value1; dataDescrs[0].Size = 4; dataDescrs[0].Reserved = 0; dataDescrs[1].Ptr = (ulong)&value2; dataDescrs[1].Size = 4; dataDescrs[1].Reserved = 0; m_provider.WriteEvent(ref m_eventData[eventId].Descriptor, 2, (IntPtr)dataDescrs); if (m_eventData[eventId].CaptureStack) CaptureStack(); } #endif if (m_OutputStreams != null && m_eventData[eventId].EnabledForAnyStream) WriteToAllStreams(eventId, value1, value2); } [Security.SecuritySafeCritical] protected unsafe void WriteEvent(int eventId, int value1, int value2, int value3) { Contract.Assert(m_eventData[eventId].Descriptor.EventId != 0); #if ETW_SUPPORTED if (m_provider != null && m_eventData[eventId].EnabledForETW) { EventProvider.EventData* dataDescrs = stackalloc EventProvider.EventData[3]; dataDescrs[0].Ptr = (ulong)&value1; dataDescrs[0].Size = 4; dataDescrs[0].Reserved = 0; dataDescrs[1].Ptr = (ulong)&value2; dataDescrs[1].Size = 4; dataDescrs[1].Reserved = 0; dataDescrs[2].Ptr = (ulong)&value3; dataDescrs[2].Size = 4; dataDescrs[2].Reserved = 0; m_provider.WriteEvent(ref m_eventData[eventId].Descriptor, 3, (IntPtr)dataDescrs); if (m_eventData[eventId].CaptureStack) CaptureStack(); } #endif if (m_OutputStreams != null && m_eventData[eventId].EnabledForAnyStream) WriteToAllStreams(eventId, value1, value2, value3); } // optimized for common signatures (longs) [Security.SecuritySafeCritical] protected unsafe void WriteEvent(int eventId, long value) { Contract.Assert(m_eventData[eventId].Descriptor.EventId != 0); #if ETW_SUPPORTED if (m_provider != null && m_eventData[eventId].EnabledForETW) { EventProvider.EventData* dataDescrs = stackalloc EventProvider.EventData[1]; dataDescrs[0].Ptr = (ulong)&value; dataDescrs[0].Size = 8; dataDescrs[0].Reserved = 0; m_provider.WriteEvent(ref m_eventData[eventId].Descriptor, 1, (IntPtr)dataDescrs); if (m_eventData[eventId].CaptureStack) CaptureStack(); } #endif if (m_OutputStreams != null && m_eventData[eventId].EnabledForAnyStream) WriteToAllStreams(eventId, value); } [Security.SecuritySafeCritical] protected unsafe void WriteEvent(int eventId, long value1, long value2) { Contract.Assert(m_eventData[eventId].Descriptor.EventId != 0); #if ETW_SUPPORTED if (m_provider != null && m_eventData[eventId].EnabledForETW) { EventProvider.EventData* dataDescrs = stackalloc EventProvider.EventData[2]; dataDescrs[0].Ptr = (ulong)&value1; dataDescrs[0].Size = 8; dataDescrs[0].Reserved = 0; dataDescrs[1].Ptr = (ulong)&value2; dataDescrs[1].Size = 8; dataDescrs[1].Reserved = 0; m_provider.WriteEvent(ref m_eventData[eventId].Descriptor, 2, (IntPtr)dataDescrs); if (m_eventData[eventId].CaptureStack) CaptureStack(); } #endif if (m_OutputStreams != null && m_eventData[eventId].EnabledForAnyStream) WriteToAllStreams(eventId, value1, value2); } [Security.SecuritySafeCritical] protected unsafe void WriteEvent(int eventId, long value1, long value2, long value3) { Contract.Assert(m_eventData[eventId].Descriptor.EventId != 0); #if ETW_SUPPORTED if (m_provider != null && m_eventData[eventId].EnabledForETW) { EventProvider.EventData* dataDescrs = stackalloc EventProvider.EventData[3]; dataDescrs[0].Ptr = (ulong)&value1; dataDescrs[0].Size = 8; dataDescrs[0].Reserved = 0; dataDescrs[1].Ptr = (ulong)&value2; dataDescrs[1].Size = 8; dataDescrs[1].Reserved = 0; dataDescrs[2].Ptr = (ulong)&value3; dataDescrs[2].Size = 8; dataDescrs[2].Reserved = 0; m_provider.WriteEvent(ref m_eventData[eventId].Descriptor, 3, (IntPtr)dataDescrs); if (m_eventData[eventId].CaptureStack) CaptureStack(); } #endif if (m_OutputStreams != null && m_eventData[eventId].EnabledForAnyStream) WriteToAllStreams(eventId, value1, value2, value3); } // optimized for common signatures (strings) [Security.SecuritySafeCritical] protected unsafe void WriteEvent(int eventId, string value) { Contract.Assert(m_eventData[eventId].Descriptor.EventId != 0); #if ETW_SUPPORTED if (m_provider != null && m_eventData[eventId].EnabledForETW) { fixed (char* stringBytes = value) { EventProvider.EventData* dataDescrs = stackalloc EventProvider.EventData[1]; dataDescrs[0].Ptr = (ulong)stringBytes; dataDescrs[0].Size = (uint)((value.Length + 1) * 2); dataDescrs[0].Reserved = 0; m_provider.WriteEvent(ref m_eventData[eventId].Descriptor, 1, (IntPtr)dataDescrs); if (m_eventData[eventId].CaptureStack) CaptureStack(); } } #endif if (m_OutputStreams != null && m_eventData[eventId].EnabledForAnyStream) WriteToAllStreams(eventId, value); } [Security.SecuritySafeCritical] protected unsafe void WriteEvent(int eventId, string value1, string value2) { Contract.Assert(m_eventData[eventId].Descriptor.EventId != 0); #if ETW_SUPPORTED if (m_provider != null && m_eventData[eventId].EnabledForETW) { fixed (char* string1Bytes = value1) fixed (char* string2Bytes = value2) { EventProvider.EventData* dataDescrs = stackalloc EventProvider.EventData[2]; dataDescrs[0].Ptr = (ulong)string1Bytes; dataDescrs[0].Size = (uint)((value1.Length + 1) * 2); dataDescrs[0].Reserved = 0; dataDescrs[1].Ptr = (ulong)string2Bytes; dataDescrs[1].Size = (uint)((value2.Length + 1) * 2); dataDescrs[1].Reserved = 0; m_provider.WriteEvent(ref m_eventData[eventId].Descriptor, 2, (IntPtr)dataDescrs); if (m_eventData[eventId].CaptureStack) CaptureStack(); } } #endif if (m_OutputStreams != null && m_eventData[eventId].EnabledForAnyStream) WriteToAllStreams(eventId, value1, value2); } [Security.SecuritySafeCritical] protected unsafe void WriteEvent(int eventId, string value1, string value2, string value3) { Contract.Assert(m_eventData[eventId].Descriptor.EventId != 0); #if ETW_SUPPORTED if (m_provider != null && m_eventData[eventId].EnabledForETW) { fixed (char* string1Bytes = value1) fixed (char* string2Bytes = value2) fixed (char* string3Bytes = value3) { EventProvider.EventData* dataDescrs = stackalloc EventProvider.EventData[3]; dataDescrs[0].Ptr = (ulong)string1Bytes; dataDescrs[0].Size = (uint)((value1.Length + 1) * 2); dataDescrs[0].Reserved = 0; dataDescrs[1].Ptr = (ulong)string2Bytes; dataDescrs[1].Size = (uint)((value2.Length + 1) * 2); dataDescrs[1].Reserved = 0; dataDescrs[2].Ptr = (ulong)string3Bytes; dataDescrs[2].Size = (uint)((value3.Length + 1) * 2); dataDescrs[2].Reserved = 0; m_provider.WriteEvent(ref m_eventData[eventId].Descriptor, 3, (IntPtr)dataDescrs); if (m_eventData[eventId].CaptureStack) CaptureStack(); } } #endif if (m_OutputStreams != null && m_eventData[eventId].EnabledForAnyStream) WriteToAllStreams(eventId, value1, value2, value3); } // optimized for common signatures (string and ints) [Security.SecuritySafeCritical] protected unsafe void WriteEvent(int eventId, string value1, int value2) { Contract.Assert(m_eventData[eventId].Descriptor.EventId != 0); #if ETW_SUPPORTED if (m_provider != null && m_eventData[eventId].EnabledForETW) { fixed (char* string1Bytes = value1) { EventProvider.EventData* dataDescrs = stackalloc EventProvider.EventData[2]; dataDescrs[0].Ptr = (ulong)string1Bytes; dataDescrs[0].Size = (uint)((value1.Length + 1) * 2); dataDescrs[0].Reserved = 0; dataDescrs[1].Ptr = (ulong)&value2; dataDescrs[1].Size = 4; dataDescrs[1].Reserved = 0; m_provider.WriteEvent(ref m_eventData[eventId].Descriptor, 2, (IntPtr)dataDescrs); if (m_eventData[eventId].CaptureStack) CaptureStack(); } } #endif if (m_OutputStreams != null && m_eventData[eventId].EnabledForAnyStream) WriteToAllStreams(eventId, value1, value2); } [Security.SecuritySafeCritical] protected unsafe void WriteEvent(int eventId, string value1, int value2, int value3) { Contract.Assert(m_eventData[eventId].Descriptor.EventId != 0); #if ETW_SUPPORTED if (m_provider != null && m_eventData[eventId].EnabledForETW) { fixed (char* string1Bytes = value1) { EventProvider.EventData* dataDescrs = stackalloc EventProvider.EventData[3]; dataDescrs[0].Ptr = (ulong)string1Bytes; dataDescrs[0].Size = (uint)((value1.Length + 1) * 2); dataDescrs[0].Reserved = 0; dataDescrs[1].Ptr = (ulong)&value2; dataDescrs[1].Size = 4; dataDescrs[1].Reserved = 0; dataDescrs[2].Ptr = (ulong)&value3; dataDescrs[2].Size = 4; dataDescrs[2].Reserved = 0; m_provider.WriteEvent(ref m_eventData[eventId].Descriptor, 3, (IntPtr)dataDescrs); if (m_eventData[eventId].CaptureStack) CaptureStack(); } } #endif if (m_OutputStreams != null && m_eventData[eventId].EnabledForAnyStream) WriteToAllStreams(eventId, value1, value2, value3); } // optimized for common signatures (string and longs) [Security.SecuritySafeCritical] protected unsafe void WriteEvent(int eventId, string value1, long value2) { Contract.Assert(m_eventData[eventId].Descriptor.EventId != 0); #if ETW_SUPPORTED if (m_provider != null && m_eventData[eventId].EnabledForETW) { fixed (char* string1Bytes = value1) { EventProvider.EventData* dataDescrs = stackalloc EventProvider.EventData[2]; dataDescrs[0].Ptr = (ulong)string1Bytes; dataDescrs[0].Size = (uint)((value1.Length + 1) * 2); dataDescrs[0].Reserved = 0; dataDescrs[1].Ptr = (ulong)&value2; dataDescrs[1].Size = 8; dataDescrs[1].Reserved = 0; m_provider.WriteEvent(ref m_eventData[eventId].Descriptor, 2, (IntPtr)dataDescrs); if (m_eventData[eventId].CaptureStack) CaptureStack(); } } #endif if (m_OutputStreams != null && m_eventData[eventId].EnabledForAnyStream) WriteToAllStreams(eventId, value1, value2); } // fallback varags helpers. [Security.SecuritySafeCritical] protected unsafe void WriteEvent(int eventId, params object[] args) { Contract.Assert(m_eventData[eventId].Descriptor.EventId != 0); #if ETW_SUPPORTED if (m_provider != null && m_eventData[eventId].EnabledForETW) m_provider.WriteEvent(ref m_eventData[eventId].Descriptor, args); #endif if (m_OutputStreams != null && m_eventData[eventId].EnabledForAnyStream) WriteToAllStreams(eventId, args); } [Security.SecuritySafeCritical] protected void WriteTransferEventHelper(int eventId, Guid relatedActivityId, params object[] args) { Contract.Assert(m_eventData[eventId].Descriptor.EventId != 0); #if ETW_SUPPORTED if (m_provider != null && m_eventData[eventId].EnabledForETW) m_provider.WriteTransferEvent(ref m_eventData[eventId].Descriptor, relatedActivityId, args); #endif // if (m_OutputStreams != null && m_eventData[eventId].EnabledForAnyStream) WriteToAllStreams(0, args); } // This method is intended to allow users to get at the 'raw' helpers if they need fine control // over the event descriptor. [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1045:DoNotPassTypesByReference", MessageId = "0#")] protected unsafe void WriteEvent(ref EventDescriptorInternal descriptor, params object[] args) { Contract.Assert(m_eventData[descriptor.EventId].Descriptor.EventId != 0); #if ETW_SUPPORTED if (m_provider != null && m_eventData[descriptor.EventId].EnabledForETW) m_provider.WriteEvent(ref descriptor, args); #endif if (m_OutputStreams != null && m_eventData[descriptor.EventId].EnabledForAnyStream) WriteToAllStreams(descriptor.EventId, args); } /// /// Returns a number one greater than the largest event Id for the provider. /// protected internal int EventIdLimit { get { InsureInitialized(); return m_eventData.Length; } } protected void SetEnabled(EventProviderDataStream outputStream, int eventId, bool value) { if (outputStream == null) { Contract.Assert(m_providerEnabled || !value, "m_providerEnabled || !value"); m_eventData[eventId].EnabledForETW = value; } else { outputStream.m_EventEnabled[eventId] = value; if (value) { m_providerEnabled = true; m_eventData[eventId].EnabledForAnyStream = true; } else { m_eventData[eventId].EnabledForAnyStream = false; for (EventProviderDataStream stream = m_OutputStreams; stream != null; stream = stream.m_Next) if (stream.m_EventEnabled[eventId]) { m_eventData[eventId].EnabledForAnyStream = true; break; } } } } protected bool IsEnabled(EventProviderDataStream outputStream, int eventId) { return outputStream.m_EventEnabled[eventId]; } ////// This indicates whether the subclass is a debug build or not (whether more validation should be /// done). By default it will do validation. It is suggested that for release builds you override /// this function to return false. /// protected virtual bool DoDebugChecks() { return true; } #endregion #region IDisposable Members public void Dispose() { this.Dispose(true); GC.SuppressFinalize(this); } ////// Disposes of an EventProvider. /// ////// Called from Dispose() with disposing=true, and from the finalizer (~MeasurementBlock) with disposing=false. /// Guidelines: /// 1. We may be called more than once: do nothing after the first call. /// 2. Avoid throwing exceptions if disposing is false, i.e. if we're being finalized. /// /// True if called from Dispose(), false if called from the finalizer. protected virtual void Dispose(bool disposing) { if (disposing) { #if ETW_SUPPORTED if (m_provider != null) { m_provider.Dispose(); m_provider = null; } #endif } } ~EventProviderBase() { this.Dispose(false); } #endregion #region private private void WriteToAllStreams(int eventId, params object[] args) { m_eventCallbackArgs.EventId = eventId; m_eventCallbackArgs.Payload = args; for (EventProviderDataStream outputStream = m_OutputStreams; outputStream != null; outputStream = outputStream.m_Next) { if (outputStream.m_EventEnabled[eventId]) outputStream.m_Callback(m_eventCallbackArgs); } if (m_eventData[eventId].CaptureStack) CaptureStack(); } // Send out an event that captures the stack trace. private void CaptureStack() { // } ////// Returns true if 'eventNum' is enabled if you only consider the levle and matchAnyKeyword filters. /// It is possible that providers turn off the event based on additional filtering criteria. /// private bool IsEnabledDefault(int eventNum, EventLevel currentLevel, EventKeywords currentMatchAnyKeyword) { if (!m_providerEnabled) return false; EventLevel eventLevel = (EventLevel)m_eventData[eventNum].Descriptor.Level; EventKeywords eventKeywords = (EventKeywords)m_eventData[eventNum].Descriptor.Keywords; if ((eventLevel <= currentLevel) || (currentLevel == 0)) { if ((eventKeywords == 0) || ((eventKeywords & currentMatchAnyKeyword) != 0)) return true; } return false; } #if ETW_SUPPORTED ////// This class lets us hook the 'OnControllerCommand' from the provider. /// private class OverideEventProvider : EventProvider { public OverideEventProvider(EventProviderBase eventProvider) { this.m_eventProviderBase = eventProvider; } protected override void OnControllerCommand(ControllerCommand command, IDictionaryarguments) { // We use null to represent the ETW EventProviderDataStream. We may want change this if it // is too confusing, but it avoids making another sentinal. EventProviderDataStream etwStream = null; m_eventProviderBase.SendCommand(etwStream, IsEnabled(), Level, MatchAnyKeyword, command, arguments); } private EventProviderBase m_eventProviderBase; } #endif /// /// Used to hold all the static information about an event. This includes everything in the event /// descriptor as well as some stuff we added specifically for EventProviderBase. see the /// code:m_eventData for where we use this. /// internal struct EventData { public EventDescriptorInternal Descriptor; public string Message; public bool EnabledForAnyStream; // true if any stream has this event turned on public bool EnabledForETW; // is this event on for the OS ETW data stream? public bool CaptureStack; // Should we caputure stack traces for this event? }; [System.Security.SecuritySafeCritical] internal void SendCommand(EventProviderDataStream outputStream, bool enable, EventLevel level, EventKeywords matchAnyKeyword, ControllerCommand command, IDictionarycommandArguments) { InsureInitialized(); if (m_OutputStreams != null && m_eventCallbackArgs == null) m_eventCallbackArgs = new EventWrittenEventArgs(this); m_providerEnabled = enable; m_level = level; m_matchAnyKeyword = matchAnyKeyword; // Find the per-Provider stream cooresponding to registered outputStream EventProviderDataStream providerOutputStream = m_OutputStreams; if (providerOutputStream != null) { for (; ; ) { if (providerOutputStream == null) throw new ArgumentException("outputStream not found"); if (providerOutputStream.m_MasterStream == outputStream) break; providerOutputStream = providerOutputStream.m_Next; } } if (enable) { // Send the manifest if the stream once per stream if (providerOutputStream != null) { if (!providerOutputStream.m_ManifestSent) { providerOutputStream.m_ManifestSent = true; SendManifest(m_rawManifest, providerOutputStream); } } else { // providerOutputStream == null means this is the ETW manifest // If we are not completely initalized we can't send the manifest because WriteEvent // will fail (handle was not returned from OS API). We will try again after the // constuctor completes. if (!m_ETWManifestSent && m_completelyInited) { m_ETWManifestSent = true; SendManifest(m_rawManifest, providerOutputStream); } } } else { // if (providerOutputStream != null) providerOutputStream.m_ManifestSent = false; else m_ETWManifestSent = false; } // Set it up using the 'standard' filtering bitfields for (int i = 0; i < m_eventData.Length; i++) SetEnabled(providerOutputStream, i, IsEnabledDefault(i, level, matchAnyKeyword)); if (commandArguments == null) commandArguments = new Dictionary (); // Allow subclasses to fiddle with it from there. OnControllerCommand(providerOutputStream, command, commandArguments); } [System.Security.SecuritySafeCritical] private void InsureInitialized() { // if (m_rawManifest == null) { lock (this) { if (m_rawManifest == null) { Contract.Assert(m_rawManifest == null); m_rawManifest = CreateManifestAndDescriptors(""); } } } } [System.Security.SecuritySafeCritical] private unsafe bool SendManifest(byte[] rawManifest, EventProviderDataStream outputStream) { fixed (byte* dataPtr = rawManifest) { EventDescriptorInternal manifestDescr = new EventDescriptorInternal(0xFFFE, 1, 0, 0, 0xFE, 0, -1); ManifestEnvelope envelope = new ManifestEnvelope(); envelope.Format = ManifestEnvelope.ManifestFormats.SimpleXmlFormat; envelope.MajorVersion = 1; envelope.MinorVersion = 0; envelope.Magic = 0x5B; // An unusual number that can be checked for consistancy. int dataLeft = rawManifest.Length; envelope.TotalChunks = (ushort)((dataLeft + (ManifestEnvelope.MaxChunkSize - 1)) / ManifestEnvelope.MaxChunkSize); envelope.ChunkNumber = 0; EventProvider.EventData* dataDescrs = stackalloc EventProvider.EventData[2]; dataDescrs[0].Ptr = (ulong)&envelope; dataDescrs[0].Size = (uint)sizeof(ManifestEnvelope); dataDescrs[0].Reserved = 0; dataDescrs[1].Ptr = (ulong)dataPtr; dataDescrs[1].Reserved = 0; bool success = true; while (dataLeft > 0) { dataDescrs[1].Size = (uint)Math.Min(dataLeft, ManifestEnvelope.MaxChunkSize); #if ETW_SUPPORTED if (outputStream == null && m_provider != null && !m_provider.WriteEvent(ref manifestDescr, 2, (IntPtr)dataDescrs)) success = false; #endif if (outputStream != null) { byte[] envelopeBlob = null; byte[] manifestBlob = null; if (envelopeBlob == null) { envelopeBlob = new byte[dataDescrs[0].Size]; manifestBlob = new byte[dataDescrs[1].Size]; } System.Runtime.InteropServices.Marshal.Copy((IntPtr)dataDescrs[0].Ptr, envelopeBlob, 0, (int)dataDescrs[0].Size); System.Runtime.InteropServices.Marshal.Copy((IntPtr)dataDescrs[1].Ptr, manifestBlob, 0, (int)dataDescrs[1].Size); m_eventCallbackArgs.EventId = manifestDescr.EventId; m_eventCallbackArgs.Payload = new object[] { envelopeBlob, manifestBlob }; outputStream.m_Callback(m_eventCallbackArgs); } dataLeft -= ManifestEnvelope.MaxChunkSize; envelope.ChunkNumber++; } return success; } } [System.Security.SecuritySafeCritical] private byte[] CreateManifestAndDescriptors(string providerDllName) { Type providerType = this.GetType(); MethodInfo[] methods = providerType.GetMethods(BindingFlags.DeclaredOnly | BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance); EventAttribute defaultEventAttribute = new EventAttribute(0); int eventId = 1; // The number given to an event that does not have a explicitly given ID. m_eventData = new EventData[methods.Length]; ManifestBuilder manifest = new ManifestBuilder(Name, Guid, providerDllName); // Collect task, opcode, keyword and channel information FieldInfo[] staticFields = providerType.GetFields(BindingFlags.DeclaredOnly | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static); if (staticFields.Length > 0) { foreach (FieldInfo staticField in staticFields) { Type staticFieldType = staticField.FieldType; if (staticFieldType == typeof(EventOpcode)) manifest.AddOpcode(staticField.Name, (int)staticField.GetRawConstantValue()); else if (staticFieldType == typeof(EventTask)) manifest.AddTask(staticField.Name, (int)staticField.GetRawConstantValue()); else if (staticFieldType == typeof(EventKeywords)) manifest.AddKeyword(staticField.Name, (ulong)(long)staticField.GetRawConstantValue()); else if (staticFieldType == typeof(EventChannel)) manifest.AddChannel(staticField.Name, (int)staticField.GetRawConstantValue()); } } for (int i = 0; i < methods.Length; i++) { MethodInfo method = methods[i]; ParameterInfo[] args = method.GetParameters(); // Get the EventDescriptorInternal (from the Custom attributes) EventAttribute eventAttribute = (EventAttribute)Attribute.GetCustomAttribute(method, typeof(EventAttribute), false); // Methods that don't return void can't be events. if (method.ReturnType != typeof(void)) { if (eventAttribute != null && DoDebugChecks()) throw new ArgumentException("Event attribute placed on method " + method.Name + " which does not return 'void'"); continue; } if (method.IsVirtual || method.IsStatic) { continue; } if (eventAttribute == null) { // If we explictly mark the method as not being an event, then honor that. if (Attribute.GetCustomAttribute(method, typeof(NonEventAttribute), false) != null) continue; defaultEventAttribute.EventId = eventId; defaultEventAttribute.Opcode = EventOpcode.Info; defaultEventAttribute.Task = EventTask.None; eventAttribute = defaultEventAttribute; } else if (eventAttribute.EventId <= 0) throw new ArgumentException("Event IDs <= 0 are illegal."); eventId++; if (eventAttribute.Opcode == EventOpcode.Info && eventAttribute.Task == EventTask.None) eventAttribute.Opcode = (EventOpcode)(10 + eventAttribute.EventId); manifest.StartEvent(method.Name, eventAttribute); for (int fieldIdx = 0; fieldIdx < args.Length; fieldIdx++) manifest.AddEventParameter(args[fieldIdx].ParameterType, args[fieldIdx].Name); manifest.EndEvent(); if (DoDebugChecks()) DebugCheckEvent(method, eventAttribute); AddEventDescriptor(eventAttribute); } TrimEventDescriptors(); m_eventsByName = null; return manifest.CreateManifest(); } [System.Security.SecuritySafeCritical] private void AddEventDescriptor(EventAttribute eventAttribute) { if (m_eventData == null || m_eventData.Length <= eventAttribute.EventId) { EventData[] newValues = new EventData[m_eventData.Length + 16]; Array.Copy(m_eventData, newValues, m_eventData.Length); m_eventData = newValues; } m_eventData[eventAttribute.EventId].Descriptor = new EventDescriptorInternal( eventAttribute.EventId, eventAttribute.Version, (byte)eventAttribute.Channel, (byte)eventAttribute.Level, (byte)eventAttribute.Opcode, (int)eventAttribute.Task, (long)eventAttribute.Keywords); m_eventData[eventAttribute.EventId].CaptureStack = eventAttribute.CaptureStack; m_eventData[eventAttribute.EventId].Message = eventAttribute.Message; } [System.Security.SecuritySafeCritical] private void TrimEventDescriptors() { int idx = m_eventData.Length; while (0 < idx) { --idx; if (m_eventData[idx].Descriptor.EventId != 0) break; } if (m_eventData.Length - idx > 2) // allow one wasted slot. { EventData[] newValues = new EventData[idx + 1]; Array.Copy(m_eventData, newValues, newValues.Length); m_eventData = newValues; } } private void DebugCheckEvent(MethodInfo method, EventAttribute eventAttribute) { int eventArg = GetHelperCallFirstArg(method); if (eventArg >= 0 && eventAttribute.EventId != eventArg) { throw new ArgumentException("Error: event " + method.Name + " is given event ID " + eventAttribute.EventId + " but " + eventArg + " was passed to the helper."); } if (eventAttribute.EventId < m_eventData.Length && m_eventData[eventAttribute.EventId].Descriptor.EventId != 0) { throw new ArgumentException("Event " + method.Name + " has ID " + eventAttribute.EventId + " which is the same as a previously defined event."); } if (m_eventsByName == null) m_eventsByName = new Dictionary (); if (m_eventsByName.ContainsKey(method.Name)) throw new ArgumentException("Event name " + method.Name + " used more than once. " + "If you wish to overload a method, the overloaded method should have a " + "[Event(-1)] attribute to indicate the method should not have associated meta-data."); m_eventsByName[method.Name] = method.Name; } /// /// This method looks at the IL and tries to pattern match against the standard /// 'boilerplate' event body /// /// { if (Enabled()) WriteEvent(#, ...) } /// /// If the pattern matches, it returns the literal number passed as the first parameter to /// the WriteEvent. This is used to find common user errors (mismatching this /// number with the EventAttribute ID). It is only used for validation. /// /// The method to probe. ///The literal value or -1 if the value could not be determined. [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Maintainability", "CA1502:AvoidExcessiveComplexity", Justification = "Switch statement is clearer than alternatives")] static private int GetHelperCallFirstArg(MethodInfo method) { // Currently searches for the following pattern // // ... // CAN ONLY BE THE INSTRUCTIONS BELOW // LDARG0 // LDC.I4 XXX // ... // CAN ONLY BE THE INSTRUCTIONS BELOW CAN'T BE A BRANCH OR A CALL // CALL // NOP // 0 or more times // RET // // If we find this pattern we return the XXX. Otherwise we return -1. byte[] instrs = method.GetMethodBody().GetILAsByteArray(); int retVal = -1; for (int idx = 0; idx < instrs.Length; ) { switch (instrs[idx]) { case 0: // NOP case 1: // BREAK case 2: // LDARG_0 case 3: // LDARG_1 case 4: // LDARG_2 case 5: // LDARG_3 case 6: // LDLOC_0 case 7: // LDLOC_1 case 8: // LDLOC_2 case 9: // LDLOC_3 case 10: // STLOC_0 case 11: // STLOC_1 case 12: // STLOC_2 case 13: // STLOC_3 break; case 14: // LDARG_S case 16: // STARG_S idx++; break; case 20: // LDNULL break; case 21: // LDC_I4_M1 case 22: // LDC_I4_0 case 23: // LDC_I4_1 case 24: // LDC_I4_2 case 25: // LDC_I4_3 case 26: // LDC_I4_4 case 27: // LDC_I4_5 case 28: // LDC_I4_6 case 29: // LDC_I4_7 case 30: // LDC_I4_8 if (idx > 0 && instrs[idx - 1] == 2) // preceeded by LDARG0 retVal = instrs[idx] - 22; break; case 31: // LDC_I4_S if (idx > 0 && instrs[idx - 1] == 2) // preceeded by LDARG0 retVal = instrs[idx + 1]; idx++; break; case 32: // LDC_I4 idx += 4; break; case 37: // DUP break; case 40: // CALL idx += 4; if (retVal >= 0) { // Is this call just before return? for (int search = idx + 1; search < instrs.Length; search++) { if (instrs[search] == 42) // RET return retVal; if (instrs[search] != 0) // NOP break; } } retVal = -1; break; case 44: // BRFALSE_S case 45: // BRTRUE_S retVal = -1; idx++; break; case 57: // BRFALSE case 58: // BRTRUE retVal = -1; idx += 4; break; case 103: // CONV_I1 case 104: // CONV_I2 case 105: // CONV_I4 case 106: // CONV_I8 case 109: // CONV_U4 case 110: // CONV_U8 break; case 140: // BOX case 141: // NEWARR idx += 4; break; case 162: // STELEM_REF break; case 254: // PREFIX idx++; // Covers the CEQ instructions used in debug code for some reason. if (idx >= instrs.Length || instrs[idx] >= 6) goto default; break; default: // Contract.Assert(false, "Warning: User validation code sub-optimial: Unsuported opcode " + instrs[idx] + // " at " + idx + " in method " + method.Name); return -1; } idx++; } return -1; } // private instance state private string m_name; // My friendly name (privided in ctor) internal int m_id; // A small integer that is unique to this instance. private Guid m_guid; // GUID representing the ETW providerBase to the OS. internal EventData[] m_eventData; // All per-event data private byte[] m_rawManifest; // Bytes to send out representing the event schema // Enabling bits private bool m_providerEnabled; // am I enabled (any of my events are enabled) private bool m_ETWManifestSent; // we could not send the ETW manifest as an event in the callback private bool m_completelyInited; // The EventProviderBase constructor has returned. internal EventLevel m_level; // lowest level enabled by any output stream internal EventKeywords m_matchAnyKeyword;// the logical OR of all levels enabled by any output stream. private EventWrittenEventArgs m_eventCallbackArgs; // Passed to the callback (Basically reusing the instance again and again) internal EventProviderDataStream m_OutputStreams; // Linked list of streams we write the data to (we also do ETW specially) #if ETW_SUPPORTED private OverideEventProvider m_provider; // We have special support for ETW. #endif // m_eventsByName is on used for error checking, when DoDebugChecks indicate validation // should be done. In this case m_eventsByName is just a set of events names that // currently exist (value is same as key). Dictionarym_eventsByName; #endregion } /// /// An EventDataStream represents the 'output' stream generated from all subclasses of EventProviderBase in the /// current appdomain. The delegate that was passed to the 'Register' method will be called back each /// time an event is logged if the provider has been turned on for that stream. /// /// Each stream is logically independent from the other streams. Each stream can send commands to the /// providers (using the SendCommand method), and in those commands set filtering criteria. The /// filtering criteria from one stream does not affect other streams. /// /// Logging be turned off by using 'SendCommand'. When the stream is no longer going to be used the 'UnRegister' /// method will disconnect the stream from the event providers. /// internal class EventProviderDataStream : IDisposable { public static EventProviderDataStream Register(ActioncallBack) { lock (EventProviderStreamsLock) { // Add the outputStream to the global list of EventProviderDataStreams. EventProviderDataStream ret = new EventProviderDataStream(callBack, s_Streams, null, null); s_Streams = ret; // Add the outputStream to each existing EventProvider. foreach (WeakReference providerRef in s_providers) { EventProviderBase providerBase = providerRef.Target as EventProviderBase; if (providerBase != null) providerBase.m_OutputStreams = new EventProviderDataStream(callBack, providerBase.m_OutputStreams, new bool[providerBase.EventIdLimit], ret); } return ret; } } public void UnRegister() { lock (EventProviderStreamsLock) { if (s_Streams != null) { if (this == s_Streams) s_Streams = this.m_Next; else { // Remove 'this' from the s_Streams linked list. EventProviderDataStream prev = s_Streams; for (; ; ) { EventProviderDataStream cur = prev.m_Next; if (cur == null) break; if (cur == this) { prev.m_Next = cur.m_Next; // Remove entry. RemoveStreamFromProviders(cur); break; } prev = cur; } } } } } /// /// A small integer (suitable for indexing in an array) identifying this provider. It is unique /// per-appdomain. This allows the callback registered in 'Register' to efficiently attach addition /// information to a provider. /// public static int ProviderId(EventProviderBase provider) { return provider.m_id; } ////// Send a command to a provider (from a particular stream). The most important command is simply /// to turn a particular provider on or off. However particular providers might implement /// additional commands (like dumping particular data structures in response to a command. /// /// The GUID for the event provider that the command is to be sent to. /// Guid.Empty is defined to be a wildcard that means all providers. /// Indicates if the provider is to be turned on or off. The rest of the /// arguments are only appicable if enabled=true. /// The level (verbosity) of the logging desired. /// Providers define groups of events and each such group is given a /// bit in a 64 bit enumeration. This parameter indicates which groups should be truned on. Events /// have are not assigned any group are never filtered by the matchAnyKeyword value. /// The command to be given to the provider. Typically it is Command.Update. /// Individual providers can define their own commands /// This is a set of key-value pairs that are interpreted by the /// provider. It is a way of passing arbitrary arguments to the provider when issuing a command public void SendCommand(Guid providerGuid, bool enable, EventLevel level, EventKeywords matchAnyKeyword, ControllerCommand command, IDictionarycommandArguments) { // Copy it to list so I can make the callback without holding the EventProviderStreamsLock. List providerBases = new List (); lock (EventProviderStreamsLock) { foreach (WeakReference providerRef in s_providers) { EventProviderBase providerBase = providerRef.Target as EventProviderBase; if (providerBase != null && (providerBase.Guid == providerGuid || providerGuid == Guid.Empty)) providerBases.Add(providerBase); } } foreach (EventProviderBase providerBase in providerBases) providerBase.SendCommand(this, enable, level, matchAnyKeyword, command, commandArguments); } public void SendCommand(Guid providerGuid, bool enable, EventLevel level) { SendCommand(providerGuid, enable, level, EventKeywords.None, ControllerCommand.Update, null); } /// /// The code:EventProviderCreated is fired whenever provider (a subclass of /// code:EventProviderBase) is created. Notifications from this event are 'retroactive, /// meaning that when a new handler is added, a callback is immediately issued for all /// providers that were created before the callback was registered. /// public static event EventHandlerEventProviderCreated { add { List providerBases = new List (); lock (EventProviderStreamsLock) { // Add new delegate to the s_EventProviderCreated = (EventHandler )Delegate.Combine(s_EventProviderCreated, value); // Make a copy so I can send events outside the lock foreach (WeakReference providerRef in s_providers) { EventProviderBase providerBase = providerRef.Target as EventProviderBase; if (providerBase != null) providerBases.Add(providerBase); } } // Send the callback for all existing eventsProviders to 'catch up' (outside the lock) foreach (EventProviderBase providerBase in providerBases) value(null, new EventProviderCreatedEventArgs { Provider = providerBase }); } remove { s_EventProviderCreated = (EventHandler )Delegate.Remove(s_EventProviderCreated, value); } } #region private void IDisposable.Dispose() { UnRegister(); } /// /// This routine adds this to the global list of providers, it also assigns the ID to the /// provider (which is simply the oridinal in the global list) /// /// internal static void AddProvider(EventProviderBase newProvider) { lock (EventProviderStreamsLock) { if (s_providers == null) s_providers = new List(2); // Periodically search the list for existing entries to reuse, this avoids // unbounded memory use if we keep recycling providers (an unlikely thing). int newIndex = -1; if (s_providers.Count % 64 == 63) { for (int i = 0; i < s_providers.Count; i++) { WeakReference weakRef = s_providers[i]; if (!weakRef.IsAlive) { newIndex = i; weakRef.Target = newProvider; break; } } } if (newIndex < 0) { newIndex = s_providers.Count; s_providers.Add(new WeakReference(newProvider)); } newProvider.m_id = newIndex; // Add every existing outputStream to the new Provider for (EventProviderDataStream outputStream = s_Streams; outputStream != null; outputStream = outputStream.m_Next) newProvider.m_OutputStreams = new EventProviderDataStream(outputStream.m_Callback, newProvider.m_OutputStreams, new bool[newProvider.EventIdLimit], outputStream); // Put in a local to avoid ---- with null check. EventHandler callback = s_EventProviderCreated; if (callback != null) callback(null, new EventProviderCreatedEventArgs { Provider = newProvider }); } } private void RemoveStreamFromProviders(EventProviderDataStream streamToRemove) { // Foreach existing Provider foreach (WeakReference providerRef in s_providers) { EventProviderBase providerBase = providerRef.Target as EventProviderBase; if (providerBase != null) { // Is the first output outputStream the outputStream we are removing? if (providerBase.m_OutputStreams.m_MasterStream == streamToRemove) providerBase.m_OutputStreams = providerBase.m_OutputStreams.m_Next; else { // Remove 'streamToRemove' from the providerBase.m_OutputStreams linked list. EventProviderDataStream prev = providerBase.m_OutputStreams; for (; ; ) { EventProviderDataStream cur = prev.m_Next; if (cur == null) { Contract.Assert(false, "Provider did not have a registers EventProviderDataStream!"); break; } if (cur.m_MasterStream == streamToRemove) { prev.m_Next = cur.m_Next; // Remove entry. break; } prev = cur; } } } } } // These conditions need to hold most of the time. [Conditional("DEBUG")] static void Validate() { // } internal EventProviderDataStream(Action callBack, EventProviderDataStream next, bool[] eventEnabled, EventProviderDataStream masterStream) { m_Callback = callBack; m_Next = next; m_EventEnabled = eventEnabled; m_MasterStream = masterStream; } internal static object EventProviderStreamsLock { get { if (s_Lock == null) Interlocked.CompareExchange(ref s_Lock, new object(), null); return s_Lock; } } // Instance fields internal Action m_Callback; internal EventProviderDataStream m_Next; // These form a linked list internal bool m_ManifestSent; // Have we sent the manifest? internal EventProviderDataStream m_MasterStream; // If this is a per-Provider outputStream, this is a link // to the outputStream in the code:s_Streams list internal bool[] m_EventEnabled; // For every event in a Provider, // static fields internal static EventProviderDataStream s_Streams; // list of all EventProviderDataStreams in the appdomain internal static object s_Lock; // lock for s_streams; private static List s_providers; // all EventProviders in the appdomain static EventHandler s_EventProviderCreated; // delegate for creating events. #endregion } /// /// code:EventWrittenEventArgs is passed when the callback given in code:EventProviderDataStream.Register is /// fired. /// internal class EventWrittenEventArgs : EventArgs { public int EventId { get; internal set; } public object[] Payload { get; internal set; } public string Message { get { string message = null; if ((uint)EventId < (uint)m_providerBase.m_eventData.Length) message = m_providerBase.m_eventData[EventId].Message; if (message != null) return string.Format(CultureInfo.InvariantCulture, message, Payload); // else if (EventId == 0 && Payload.Length == 1 && Payload[0].GetType() == typeof(string)) // This is for WriteMessage return Payload[0].ToString(); else return null; } } public EventProviderBase Provider { get { return m_providerBase; } } public EventDescriptorInternal Descriptor { get { return m_providerBase.m_eventData[EventId].Descriptor; } } public EventLevel Level { get { if ((uint)EventId >= (uint)m_providerBase.m_eventData.Length) return EventLevel.LogAlways; return (EventLevel)m_providerBase.m_eventData[EventId].Descriptor.Level; } } #region private internal EventWrittenEventArgs(EventProviderBase providerBase) { m_providerBase = providerBase; } private EventProviderBase m_providerBase; #endregion } ////// code:EventProviderCreatedEventArgs is passed when the code:EventProviderDataStream.EventProviderCreated /// is fired. /// internal class EventProviderCreatedEventArgs : EventArgs { public EventProviderBase Provider; } ////// All instance methods in a class that subclasses code:EventProviderBasethat and return void /// are assumed to be methods that generate an event. Enough information can be deduced from the name /// of the method and its signature to generate basic schema information for the event. The /// code:EventAttribute allows you to specify additional event schema information for an event if /// desired. /// [AttributeUsage(AttributeTargets.Method)] [System.Runtime.CompilerServices.FriendAccessAllowed] internal sealed class EventAttribute : Attribute { public EventAttribute(int eventId) { this.EventId = eventId; } public int EventId { get; internal set; } public string Name { get; set; } public EventLevel Level { get; set; } public EventKeywords Keywords { get; set; } public EventOpcode Opcode { get; set; } public EventTask Task { get; set; } public EventChannel Channel { get; set; } public byte Version { get; set; } public bool CaptureStack { get; set; } ////// This is also used for m_OutputStreams compatabilty. If code:EventProviderBase.TraceSourceSupport is /// on events will also be logged a tracesource with the same name as the providerBase. If this /// property is set then the payload will go to code:TraceSource.TraceEvent, and this string /// will be used as the message. if this property is not set not set it goes to /// code:TraceSource.TraceData /// public string Message { get; set; } ////// Like Message, but you specify the resource ID of the string you wish to fetch. It will use the /// ResourceManager to look this resource up in the resources associated with the providerBase type /// public string MessageResourceId { get; set; } } ////// By default all instance methods in a class that subclasses code:EventProviderBasethat and return void /// are assumed to be methods that generate an event. This default can be overriden by specifying the /// code:NonEventAttribute /// [AttributeUsage(AttributeTargets.Method)] [System.Runtime.CompilerServices.FriendAccessAllowed] internal sealed class NonEventAttribute : Attribute { public NonEventAttribute() { } } #region private classes ////// ManifestBuilder is designed to isolate the details of the message of the event from the /// rest of EventProviderBase. This one happens to create XML. /// internal class ManifestBuilder { public ManifestBuilder(string providerName, Guid providerGuid, string dllName) { this.providerGuid = providerGuid; sb = new StringBuilder(); events = new StringBuilder(); templates = new StringBuilder(); opcodeTab = new Dictionary(); sb.Append(" ").AppendLine(); } public void AddOpcode(string name, int value) { opcodeTab[value] = name; } public void AddTask(string name, int value) { if (taskTab == null) taskTab = new Dictionary ").AppendLine(); return Encoding.UTF8.GetBytes(sb.ToString()); } #region private private static string GetLevelName(EventLevel level) { // return "win:" + level.ToString(); } private string GetChannelName(EventChannel channel, string eventName) { string ret = null; if (channelTab == null || !channelTab.TryGetValue((int)channel, out ret)) throw new ArgumentException("Use of undefined channel value " + channel + " for event " + eventName); return ret; } private string GetTaskName(EventTask task, string eventName) { string ret = null; if (taskTab == null || !taskTab.TryGetValue((int)task, out ret)) throw new ArgumentException("Use of undefined task value " + task + " for event " + eventName); return ret; } private string GetOpcodeName(EventOpcode opcode, string eventName) { switch (opcode) { case EventOpcode.Info: return "win:Info"; case EventOpcode.Start: return "win:Start"; case EventOpcode.Stop: return "win:Stop"; case EventOpcode.DataCollectionStart: return "win:DC_Start"; case EventOpcode.DataCollectionStop: return "win:DC_Stop"; case EventOpcode.Extension: return "win:Extension"; case EventOpcode.Reply: return "win:Reply"; case EventOpcode.Resume: return "win:Resume"; case EventOpcode.Suspend: return "win:Suspend"; case EventOpcode.Send: return "win:Send"; case EventOpcode.Receive: return "win:Receive"; } // string ret = null; if (opcodeTab == null) opcodeTab = new Dictionary(); taskTab[value] = name; } public void AddKeyword(string name, ulong value) { if ((value & (value - 1)) != 0) // Is it a power of 2? throw new ArgumentException("Value " + value.ToString("x", CultureInfo.CurrentCulture) + " for keyword " + name + " needs to be a power of 2."); if (keywordTab == null) keywordTab = new Dictionary (); keywordTab[value] = name; } public void AddChannel(string name, int value) { if (channelTab == null) channelTab = new Dictionary (); channelTab[value] = name; } public void StartEvent(string eventName, EventAttribute eventAttribute) { Contract.Assert(numParams == 0); Contract.Assert(templateName == null); templateName = eventName + "Args"; numParams = 0; events.Append(" ").AppendLine(); numParams++; templates.Append(" ").AppendLine(); } public void EndEvent() { if (numParams > 0) { templates.Append(" ").AppendLine(); events.Append(" template=\"").Append(templateName).Append("\""); } events.Append("/>").AppendLine(); templateName = null; numParams = 0; } public byte[] CreateManifest() { // Write out the channels if (channelTab != null) { sb.Append(" ").AppendLine(); foreach (int channel in channelTab.Keys) sb.Append(" ").AppendLine(); } // Write out the tasks if (taskTab != null) { sb.Append("").AppendLine(); sb.Append(" ").AppendLine(); foreach (int task in taskTab.Keys) { Guid taskGuid = EventProvider.GenTaskGuidFromProviderGuid(providerGuid, (ushort)task); sb.Append(" ").AppendLine(); } // Write out the opcodes sb.Append("").AppendLine(); } sb.Append(" ").AppendLine(); foreach (int opcode in opcodeTab.Keys) sb.Append(" ").AppendLine(); // Write out the keywords if (keywordTab != null) { sb.Append("").AppendLine(); sb.Append(" ").AppendLine(); foreach (ulong keyword in keywordTab.Keys) sb.Append(" ").AppendLine(); } sb.Append("").AppendLine(); sb.Append(" ").AppendLine(); sb.Append(events); sb.Append(" ").AppendLine(); if (templates.Length > 0) { sb.Append("").AppendLine(); sb.Append(templates); sb.Append(" ").AppendLine(); } sb.Append("(); if (!opcodeTab.TryGetValue((int)opcode, out ret)) opcodeTab[(int)opcode] = eventName; return eventName; } private string GetKeywords(ulong keywords, string eventName) { string ret = ""; for (ulong bit = 1; bit != 0; bit <<= 1) { if ((keywords & bit) != 0) { string keyword; if (keywordTab == null || !keywordTab.TryGetValue(bit, out keyword)) throw new ArgumentException("Use of undefined keyword value " + bit.ToString("x", CultureInfo.CurrentCulture) + " for event " + eventName); if (ret.Length != 0) ret = ret + " "; ret = ret + keyword; } } return ret; } private static string GetTypeName(Type type) { switch (Type.GetTypeCode(type)) { case TypeCode.Boolean: return "win:Boolean"; case TypeCode.Byte: return "win:Uint8"; case TypeCode.UInt16: return "win:UInt16"; case TypeCode.UInt32: return "win:UInt32"; case TypeCode.UInt64: return "win:UInt64"; case TypeCode.SByte: return "win:Int8"; case TypeCode.Int16: return "win:Int16"; case TypeCode.Int32: return "win:Int32"; case TypeCode.Int64: return "win:Int64"; case TypeCode.String: return "win:UnicodeString"; case TypeCode.Single: return "win:Float"; case TypeCode.Double: return "win:Double"; default: if (type == typeof(Guid)) return "win:GUID"; throw new ArgumentException("Unsupported type " + type.Name); } } Dictionary opcodeTab; Dictionary taskTab; Dictionary channelTab; Dictionary keywordTab; StringBuilder sb; StringBuilder events; StringBuilder templates; Guid providerGuid; string templateName; int numParams; #endregion } /// /// Used to send the m_rawManifest into the event outputStream as a series of events. /// internal struct ManifestEnvelope { public const int MaxChunkSize = 0xFF00; public enum ManifestFormats : byte { SimpleXmlFormat = 1, // Simply dump what is under thetag in an XML manifest } public ManifestFormats Format; public byte MajorVersion; public byte MinorVersion; public byte Magic; public ushort TotalChunks; public ushort ChunkNumber; }; #endregion } // File provided for Reference Use Only by Microsoft Corporation (c) 2007. //------------------------------------------------------------------------------ // // Copyright (c) Microsoft Corporation. All rights reserved. // //[....] //----------------------------------------------------------------------------- // This program uses code hyperlinks available as part of the HyperAddin Visual Studio plug-in. // It is available from http://www.codeplex.com/hyperAddin using System; using System.Collections.Generic; using System.Globalization; using System.Text; using System.Diagnostics.Eventing; using System.Diagnostics; using System.Reflection; using System.Threading; using System.Diagnostics.Contracts; // Implementation of my EventProvider scheme namespace System.Diagnostics.Eventing { ////// This class is meant to be inherited by a user provider (which provides specific events and then /// calls code:EventProviderBase.WriteEvent to log them). /// /// sealed class MinimalProvider : EventProviderBase /// { /// * public void Load(long ImageBase, string Name) { WriteEvent(1, ImageBase, Name); } /// * public void Unload(long ImageBase) { WriteEvent(2, ImageBase); } /// * private MinimalProvider() : base(new Guid(0xc836fd3, 0xee92, 0x4301, 0xbc, 0x13, 0xd8, 0x94, 0x3e, 0xc, 0x1e, 0x77)) {} /// } /// /// This functionaity is sufficient for many users. When more control is needed over the ETW manifest /// that is created, that can be done by adding [Event] attributes on the methods. /// /// Finally for very advanced Providers, it is possible to intercept the commands being given to the /// provider and change what filtering is done (or cause actions to be performed by the provider (eg /// duming a data structure). /// /// The providers can be turned on with Window ETW controllers (eg logman), immediately. It is also /// possible to control and intercept the data stream programatically. We code:EventProviderDataStream for /// more. /// [System.Runtime.CompilerServices.FriendAccessAllowed] internal class EventProviderBase : IDisposable { ////// Most events should be fired by calling methods on a subclass of code:EventProviderBase but we /// provide this API in the base to provide this trivial functionality in the case of a single /// string. /// public void WriteMessage(string eventMessage) { WriteMessage(eventMessage, EventLevel.LogAlways, EventKeywords.None); } public void WriteMessage(string eventMessage, EventLevel level, EventKeywords keywords) { #if ETW_SUPPORTED if(m_provider != null) m_provider.WriteMessageEvent(eventMessage, (byte)level, (long)keywords); #endif WriteToAllStreams(0, eventMessage); } ////// The human-friendly name of the provider. It defaults to the simple name of the class /// public string Name { get { return m_name; } } ////// Every provider is assigned a GUID to uniquely identify it to the system. /// public Guid Guid { get { return m_guid; } } ////// Returns true if the provider has been enabled at all. /// public bool IsEnabled() { return m_providerEnabled; } public bool IsEnabled(EventLevel level, EventKeywords keywords) { if (!m_providerEnabled) return false; if (m_level != 0 && m_level < level) return false; return m_matchAnyKeyword == 0 || (keywords & m_matchAnyKeyword) != 0; } ////// Returns a string of the XML manifest associated with the provider. The scheme for this XML is /// documented at in EventManifest Schema http://msdn.microsoft.com/en-us/library/aa384043(VS.85).aspx /// /// The manifest XML fragment contains the string name of the DLL name in /// which it is embeded. This parameter spcifies what name will be used ///The XML data string public string ProviderManifestXmlFragment(string providerDllName) { byte[] providerBytes = m_rawManifest; if (providerBytes == null) providerBytes = CreateManifestAndDescriptors(providerDllName); return Encoding.UTF8.GetString(providerBytes); } #region protected [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1720:IdentifiersShouldNotContainTypeNames", MessageId = "guid")] protected EventProviderBase(Guid providerGuid) : this(providerGuid, null) { } [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1720:IdentifiersShouldNotContainTypeNames", MessageId = "guid")] [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2214:DoNotCallOverridableMethodsInConstructors", Justification = "The constructor call chains to DoDebugChecks() is safe.")] [System.Security.SecuritySafeCritical] protected EventProviderBase(Guid providerGuid, string providerName) { if (providerName == null) providerName = GetType().Name; m_name = providerName; m_guid = providerGuid; #if ETW_SUPPORTED m_provider = new OverideEventProvider(this); try { m_provider.Register(providerGuid); } catch (ArgumentException) { // Failed to register. Don't crash the app, just don't write events to ETW. m_provider = null; } #endif if (m_providerEnabled && !m_ETWManifestSent) { SendManifest(m_rawManifest, null); m_ETWManifestSent = true; } m_completelyInited = true; // Add the provider to the global (weak) list. This also sets m_id, which is the // index in the list. EventProviderDataStream.AddProvider(this); } ////// This method is called when the provider is updated by the controller. /// protected virtual void OnControllerCommand(EventProviderDataStream outputStream, ControllerCommand command, IDictionaryarguments) { } // optimized for common signatures (no args) protected unsafe void WriteEvent(int eventId) { Contract.Assert(m_eventData[eventId].Descriptor.EventId != 0); #if ETW_SUPPORTED if (m_provider != null && m_eventData[eventId].EnabledForETW) { m_provider.WriteEvent(ref m_eventData[eventId].Descriptor, 0, (IntPtr)0); if (m_eventData[eventId].CaptureStack) CaptureStack(); } #endif if (m_OutputStreams != null && m_eventData[eventId].EnabledForAnyStream) WriteToAllStreams(eventId); } // optimized for common signatures (ints) [Security.SecuritySafeCritical] protected unsafe void WriteEvent(int eventId, int value) { Contract.Assert(m_eventData[eventId].Descriptor.EventId != 0); #if ETW_SUPPORTED if (m_provider != null && m_eventData[eventId].EnabledForETW) { EventProvider.EventData* dataDescrs = stackalloc EventProvider.EventData[1]; dataDescrs[0].Ptr = (ulong)&value; dataDescrs[0].Size = 4; dataDescrs[0].Reserved = 0; m_provider.WriteEvent(ref m_eventData[eventId].Descriptor, 1, (IntPtr)dataDescrs); if (m_eventData[eventId].CaptureStack) CaptureStack(); } #endif if (m_OutputStreams != null && m_eventData[eventId].EnabledForAnyStream) WriteToAllStreams(eventId, value); } [Security.SecuritySafeCritical] protected unsafe void WriteEvent(int eventId, int value1, int value2) { Contract.Assert(m_eventData[eventId].Descriptor.EventId != 0); #if ETW_SUPPORTED if (m_provider != null && m_eventData[eventId].EnabledForETW) { EventProvider.EventData* dataDescrs = stackalloc EventProvider.EventData[2]; dataDescrs[0].Ptr = (ulong)&value1; dataDescrs[0].Size = 4; dataDescrs[0].Reserved = 0; dataDescrs[1].Ptr = (ulong)&value2; dataDescrs[1].Size = 4; dataDescrs[1].Reserved = 0; m_provider.WriteEvent(ref m_eventData[eventId].Descriptor, 2, (IntPtr)dataDescrs); if (m_eventData[eventId].CaptureStack) CaptureStack(); } #endif if (m_OutputStreams != null && m_eventData[eventId].EnabledForAnyStream) WriteToAllStreams(eventId, value1, value2); } [Security.SecuritySafeCritical] protected unsafe void WriteEvent(int eventId, int value1, int value2, int value3) { Contract.Assert(m_eventData[eventId].Descriptor.EventId != 0); #if ETW_SUPPORTED if (m_provider != null && m_eventData[eventId].EnabledForETW) { EventProvider.EventData* dataDescrs = stackalloc EventProvider.EventData[3]; dataDescrs[0].Ptr = (ulong)&value1; dataDescrs[0].Size = 4; dataDescrs[0].Reserved = 0; dataDescrs[1].Ptr = (ulong)&value2; dataDescrs[1].Size = 4; dataDescrs[1].Reserved = 0; dataDescrs[2].Ptr = (ulong)&value3; dataDescrs[2].Size = 4; dataDescrs[2].Reserved = 0; m_provider.WriteEvent(ref m_eventData[eventId].Descriptor, 3, (IntPtr)dataDescrs); if (m_eventData[eventId].CaptureStack) CaptureStack(); } #endif if (m_OutputStreams != null && m_eventData[eventId].EnabledForAnyStream) WriteToAllStreams(eventId, value1, value2, value3); } // optimized for common signatures (longs) [Security.SecuritySafeCritical] protected unsafe void WriteEvent(int eventId, long value) { Contract.Assert(m_eventData[eventId].Descriptor.EventId != 0); #if ETW_SUPPORTED if (m_provider != null && m_eventData[eventId].EnabledForETW) { EventProvider.EventData* dataDescrs = stackalloc EventProvider.EventData[1]; dataDescrs[0].Ptr = (ulong)&value; dataDescrs[0].Size = 8; dataDescrs[0].Reserved = 0; m_provider.WriteEvent(ref m_eventData[eventId].Descriptor, 1, (IntPtr)dataDescrs); if (m_eventData[eventId].CaptureStack) CaptureStack(); } #endif if (m_OutputStreams != null && m_eventData[eventId].EnabledForAnyStream) WriteToAllStreams(eventId, value); } [Security.SecuritySafeCritical] protected unsafe void WriteEvent(int eventId, long value1, long value2) { Contract.Assert(m_eventData[eventId].Descriptor.EventId != 0); #if ETW_SUPPORTED if (m_provider != null && m_eventData[eventId].EnabledForETW) { EventProvider.EventData* dataDescrs = stackalloc EventProvider.EventData[2]; dataDescrs[0].Ptr = (ulong)&value1; dataDescrs[0].Size = 8; dataDescrs[0].Reserved = 0; dataDescrs[1].Ptr = (ulong)&value2; dataDescrs[1].Size = 8; dataDescrs[1].Reserved = 0; m_provider.WriteEvent(ref m_eventData[eventId].Descriptor, 2, (IntPtr)dataDescrs); if (m_eventData[eventId].CaptureStack) CaptureStack(); } #endif if (m_OutputStreams != null && m_eventData[eventId].EnabledForAnyStream) WriteToAllStreams(eventId, value1, value2); } [Security.SecuritySafeCritical] protected unsafe void WriteEvent(int eventId, long value1, long value2, long value3) { Contract.Assert(m_eventData[eventId].Descriptor.EventId != 0); #if ETW_SUPPORTED if (m_provider != null && m_eventData[eventId].EnabledForETW) { EventProvider.EventData* dataDescrs = stackalloc EventProvider.EventData[3]; dataDescrs[0].Ptr = (ulong)&value1; dataDescrs[0].Size = 8; dataDescrs[0].Reserved = 0; dataDescrs[1].Ptr = (ulong)&value2; dataDescrs[1].Size = 8; dataDescrs[1].Reserved = 0; dataDescrs[2].Ptr = (ulong)&value3; dataDescrs[2].Size = 8; dataDescrs[2].Reserved = 0; m_provider.WriteEvent(ref m_eventData[eventId].Descriptor, 3, (IntPtr)dataDescrs); if (m_eventData[eventId].CaptureStack) CaptureStack(); } #endif if (m_OutputStreams != null && m_eventData[eventId].EnabledForAnyStream) WriteToAllStreams(eventId, value1, value2, value3); } // optimized for common signatures (strings) [Security.SecuritySafeCritical] protected unsafe void WriteEvent(int eventId, string value) { Contract.Assert(m_eventData[eventId].Descriptor.EventId != 0); #if ETW_SUPPORTED if (m_provider != null && m_eventData[eventId].EnabledForETW) { fixed (char* stringBytes = value) { EventProvider.EventData* dataDescrs = stackalloc EventProvider.EventData[1]; dataDescrs[0].Ptr = (ulong)stringBytes; dataDescrs[0].Size = (uint)((value.Length + 1) * 2); dataDescrs[0].Reserved = 0; m_provider.WriteEvent(ref m_eventData[eventId].Descriptor, 1, (IntPtr)dataDescrs); if (m_eventData[eventId].CaptureStack) CaptureStack(); } } #endif if (m_OutputStreams != null && m_eventData[eventId].EnabledForAnyStream) WriteToAllStreams(eventId, value); } [Security.SecuritySafeCritical] protected unsafe void WriteEvent(int eventId, string value1, string value2) { Contract.Assert(m_eventData[eventId].Descriptor.EventId != 0); #if ETW_SUPPORTED if (m_provider != null && m_eventData[eventId].EnabledForETW) { fixed (char* string1Bytes = value1) fixed (char* string2Bytes = value2) { EventProvider.EventData* dataDescrs = stackalloc EventProvider.EventData[2]; dataDescrs[0].Ptr = (ulong)string1Bytes; dataDescrs[0].Size = (uint)((value1.Length + 1) * 2); dataDescrs[0].Reserved = 0; dataDescrs[1].Ptr = (ulong)string2Bytes; dataDescrs[1].Size = (uint)((value2.Length + 1) * 2); dataDescrs[1].Reserved = 0; m_provider.WriteEvent(ref m_eventData[eventId].Descriptor, 2, (IntPtr)dataDescrs); if (m_eventData[eventId].CaptureStack) CaptureStack(); } } #endif if (m_OutputStreams != null && m_eventData[eventId].EnabledForAnyStream) WriteToAllStreams(eventId, value1, value2); } [Security.SecuritySafeCritical] protected unsafe void WriteEvent(int eventId, string value1, string value2, string value3) { Contract.Assert(m_eventData[eventId].Descriptor.EventId != 0); #if ETW_SUPPORTED if (m_provider != null && m_eventData[eventId].EnabledForETW) { fixed (char* string1Bytes = value1) fixed (char* string2Bytes = value2) fixed (char* string3Bytes = value3) { EventProvider.EventData* dataDescrs = stackalloc EventProvider.EventData[3]; dataDescrs[0].Ptr = (ulong)string1Bytes; dataDescrs[0].Size = (uint)((value1.Length + 1) * 2); dataDescrs[0].Reserved = 0; dataDescrs[1].Ptr = (ulong)string2Bytes; dataDescrs[1].Size = (uint)((value2.Length + 1) * 2); dataDescrs[1].Reserved = 0; dataDescrs[2].Ptr = (ulong)string3Bytes; dataDescrs[2].Size = (uint)((value3.Length + 1) * 2); dataDescrs[2].Reserved = 0; m_provider.WriteEvent(ref m_eventData[eventId].Descriptor, 3, (IntPtr)dataDescrs); if (m_eventData[eventId].CaptureStack) CaptureStack(); } } #endif if (m_OutputStreams != null && m_eventData[eventId].EnabledForAnyStream) WriteToAllStreams(eventId, value1, value2, value3); } // optimized for common signatures (string and ints) [Security.SecuritySafeCritical] protected unsafe void WriteEvent(int eventId, string value1, int value2) { Contract.Assert(m_eventData[eventId].Descriptor.EventId != 0); #if ETW_SUPPORTED if (m_provider != null && m_eventData[eventId].EnabledForETW) { fixed (char* string1Bytes = value1) { EventProvider.EventData* dataDescrs = stackalloc EventProvider.EventData[2]; dataDescrs[0].Ptr = (ulong)string1Bytes; dataDescrs[0].Size = (uint)((value1.Length + 1) * 2); dataDescrs[0].Reserved = 0; dataDescrs[1].Ptr = (ulong)&value2; dataDescrs[1].Size = 4; dataDescrs[1].Reserved = 0; m_provider.WriteEvent(ref m_eventData[eventId].Descriptor, 2, (IntPtr)dataDescrs); if (m_eventData[eventId].CaptureStack) CaptureStack(); } } #endif if (m_OutputStreams != null && m_eventData[eventId].EnabledForAnyStream) WriteToAllStreams(eventId, value1, value2); } [Security.SecuritySafeCritical] protected unsafe void WriteEvent(int eventId, string value1, int value2, int value3) { Contract.Assert(m_eventData[eventId].Descriptor.EventId != 0); #if ETW_SUPPORTED if (m_provider != null && m_eventData[eventId].EnabledForETW) { fixed (char* string1Bytes = value1) { EventProvider.EventData* dataDescrs = stackalloc EventProvider.EventData[3]; dataDescrs[0].Ptr = (ulong)string1Bytes; dataDescrs[0].Size = (uint)((value1.Length + 1) * 2); dataDescrs[0].Reserved = 0; dataDescrs[1].Ptr = (ulong)&value2; dataDescrs[1].Size = 4; dataDescrs[1].Reserved = 0; dataDescrs[2].Ptr = (ulong)&value3; dataDescrs[2].Size = 4; dataDescrs[2].Reserved = 0; m_provider.WriteEvent(ref m_eventData[eventId].Descriptor, 3, (IntPtr)dataDescrs); if (m_eventData[eventId].CaptureStack) CaptureStack(); } } #endif if (m_OutputStreams != null && m_eventData[eventId].EnabledForAnyStream) WriteToAllStreams(eventId, value1, value2, value3); } // optimized for common signatures (string and longs) [Security.SecuritySafeCritical] protected unsafe void WriteEvent(int eventId, string value1, long value2) { Contract.Assert(m_eventData[eventId].Descriptor.EventId != 0); #if ETW_SUPPORTED if (m_provider != null && m_eventData[eventId].EnabledForETW) { fixed (char* string1Bytes = value1) { EventProvider.EventData* dataDescrs = stackalloc EventProvider.EventData[2]; dataDescrs[0].Ptr = (ulong)string1Bytes; dataDescrs[0].Size = (uint)((value1.Length + 1) * 2); dataDescrs[0].Reserved = 0; dataDescrs[1].Ptr = (ulong)&value2; dataDescrs[1].Size = 8; dataDescrs[1].Reserved = 0; m_provider.WriteEvent(ref m_eventData[eventId].Descriptor, 2, (IntPtr)dataDescrs); if (m_eventData[eventId].CaptureStack) CaptureStack(); } } #endif if (m_OutputStreams != null && m_eventData[eventId].EnabledForAnyStream) WriteToAllStreams(eventId, value1, value2); } // fallback varags helpers. [Security.SecuritySafeCritical] protected unsafe void WriteEvent(int eventId, params object[] args) { Contract.Assert(m_eventData[eventId].Descriptor.EventId != 0); #if ETW_SUPPORTED if (m_provider != null && m_eventData[eventId].EnabledForETW) m_provider.WriteEvent(ref m_eventData[eventId].Descriptor, args); #endif if (m_OutputStreams != null && m_eventData[eventId].EnabledForAnyStream) WriteToAllStreams(eventId, args); } [Security.SecuritySafeCritical] protected void WriteTransferEventHelper(int eventId, Guid relatedActivityId, params object[] args) { Contract.Assert(m_eventData[eventId].Descriptor.EventId != 0); #if ETW_SUPPORTED if (m_provider != null && m_eventData[eventId].EnabledForETW) m_provider.WriteTransferEvent(ref m_eventData[eventId].Descriptor, relatedActivityId, args); #endif // if (m_OutputStreams != null && m_eventData[eventId].EnabledForAnyStream) WriteToAllStreams(0, args); } // This method is intended to allow users to get at the 'raw' helpers if they need fine control // over the event descriptor. [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1045:DoNotPassTypesByReference", MessageId = "0#")] protected unsafe void WriteEvent(ref EventDescriptorInternal descriptor, params object[] args) { Contract.Assert(m_eventData[descriptor.EventId].Descriptor.EventId != 0); #if ETW_SUPPORTED if (m_provider != null && m_eventData[descriptor.EventId].EnabledForETW) m_provider.WriteEvent(ref descriptor, args); #endif if (m_OutputStreams != null && m_eventData[descriptor.EventId].EnabledForAnyStream) WriteToAllStreams(descriptor.EventId, args); } /// /// Returns a number one greater than the largest event Id for the provider. /// protected internal int EventIdLimit { get { InsureInitialized(); return m_eventData.Length; } } protected void SetEnabled(EventProviderDataStream outputStream, int eventId, bool value) { if (outputStream == null) { Contract.Assert(m_providerEnabled || !value, "m_providerEnabled || !value"); m_eventData[eventId].EnabledForETW = value; } else { outputStream.m_EventEnabled[eventId] = value; if (value) { m_providerEnabled = true; m_eventData[eventId].EnabledForAnyStream = true; } else { m_eventData[eventId].EnabledForAnyStream = false; for (EventProviderDataStream stream = m_OutputStreams; stream != null; stream = stream.m_Next) if (stream.m_EventEnabled[eventId]) { m_eventData[eventId].EnabledForAnyStream = true; break; } } } } protected bool IsEnabled(EventProviderDataStream outputStream, int eventId) { return outputStream.m_EventEnabled[eventId]; } ////// This indicates whether the subclass is a debug build or not (whether more validation should be /// done). By default it will do validation. It is suggested that for release builds you override /// this function to return false. /// protected virtual bool DoDebugChecks() { return true; } #endregion #region IDisposable Members public void Dispose() { this.Dispose(true); GC.SuppressFinalize(this); } ////// Disposes of an EventProvider. /// ////// Called from Dispose() with disposing=true, and from the finalizer (~MeasurementBlock) with disposing=false. /// Guidelines: /// 1. We may be called more than once: do nothing after the first call. /// 2. Avoid throwing exceptions if disposing is false, i.e. if we're being finalized. /// /// True if called from Dispose(), false if called from the finalizer. protected virtual void Dispose(bool disposing) { if (disposing) { #if ETW_SUPPORTED if (m_provider != null) { m_provider.Dispose(); m_provider = null; } #endif } } ~EventProviderBase() { this.Dispose(false); } #endregion #region private private void WriteToAllStreams(int eventId, params object[] args) { m_eventCallbackArgs.EventId = eventId; m_eventCallbackArgs.Payload = args; for (EventProviderDataStream outputStream = m_OutputStreams; outputStream != null; outputStream = outputStream.m_Next) { if (outputStream.m_EventEnabled[eventId]) outputStream.m_Callback(m_eventCallbackArgs); } if (m_eventData[eventId].CaptureStack) CaptureStack(); } // Send out an event that captures the stack trace. private void CaptureStack() { // } ////// Returns true if 'eventNum' is enabled if you only consider the levle and matchAnyKeyword filters. /// It is possible that providers turn off the event based on additional filtering criteria. /// private bool IsEnabledDefault(int eventNum, EventLevel currentLevel, EventKeywords currentMatchAnyKeyword) { if (!m_providerEnabled) return false; EventLevel eventLevel = (EventLevel)m_eventData[eventNum].Descriptor.Level; EventKeywords eventKeywords = (EventKeywords)m_eventData[eventNum].Descriptor.Keywords; if ((eventLevel <= currentLevel) || (currentLevel == 0)) { if ((eventKeywords == 0) || ((eventKeywords & currentMatchAnyKeyword) != 0)) return true; } return false; } #if ETW_SUPPORTED ////// This class lets us hook the 'OnControllerCommand' from the provider. /// private class OverideEventProvider : EventProvider { public OverideEventProvider(EventProviderBase eventProvider) { this.m_eventProviderBase = eventProvider; } protected override void OnControllerCommand(ControllerCommand command, IDictionaryarguments) { // We use null to represent the ETW EventProviderDataStream. We may want change this if it // is too confusing, but it avoids making another sentinal. EventProviderDataStream etwStream = null; m_eventProviderBase.SendCommand(etwStream, IsEnabled(), Level, MatchAnyKeyword, command, arguments); } private EventProviderBase m_eventProviderBase; } #endif /// /// Used to hold all the static information about an event. This includes everything in the event /// descriptor as well as some stuff we added specifically for EventProviderBase. see the /// code:m_eventData for where we use this. /// internal struct EventData { public EventDescriptorInternal Descriptor; public string Message; public bool EnabledForAnyStream; // true if any stream has this event turned on public bool EnabledForETW; // is this event on for the OS ETW data stream? public bool CaptureStack; // Should we caputure stack traces for this event? }; [System.Security.SecuritySafeCritical] internal void SendCommand(EventProviderDataStream outputStream, bool enable, EventLevel level, EventKeywords matchAnyKeyword, ControllerCommand command, IDictionarycommandArguments) { InsureInitialized(); if (m_OutputStreams != null && m_eventCallbackArgs == null) m_eventCallbackArgs = new EventWrittenEventArgs(this); m_providerEnabled = enable; m_level = level; m_matchAnyKeyword = matchAnyKeyword; // Find the per-Provider stream cooresponding to registered outputStream EventProviderDataStream providerOutputStream = m_OutputStreams; if (providerOutputStream != null) { for (; ; ) { if (providerOutputStream == null) throw new ArgumentException("outputStream not found"); if (providerOutputStream.m_MasterStream == outputStream) break; providerOutputStream = providerOutputStream.m_Next; } } if (enable) { // Send the manifest if the stream once per stream if (providerOutputStream != null) { if (!providerOutputStream.m_ManifestSent) { providerOutputStream.m_ManifestSent = true; SendManifest(m_rawManifest, providerOutputStream); } } else { // providerOutputStream == null means this is the ETW manifest // If we are not completely initalized we can't send the manifest because WriteEvent // will fail (handle was not returned from OS API). We will try again after the // constuctor completes. if (!m_ETWManifestSent && m_completelyInited) { m_ETWManifestSent = true; SendManifest(m_rawManifest, providerOutputStream); } } } else { // if (providerOutputStream != null) providerOutputStream.m_ManifestSent = false; else m_ETWManifestSent = false; } // Set it up using the 'standard' filtering bitfields for (int i = 0; i < m_eventData.Length; i++) SetEnabled(providerOutputStream, i, IsEnabledDefault(i, level, matchAnyKeyword)); if (commandArguments == null) commandArguments = new Dictionary (); // Allow subclasses to fiddle with it from there. OnControllerCommand(providerOutputStream, command, commandArguments); } [System.Security.SecuritySafeCritical] private void InsureInitialized() { // if (m_rawManifest == null) { lock (this) { if (m_rawManifest == null) { Contract.Assert(m_rawManifest == null); m_rawManifest = CreateManifestAndDescriptors(""); } } } } [System.Security.SecuritySafeCritical] private unsafe bool SendManifest(byte[] rawManifest, EventProviderDataStream outputStream) { fixed (byte* dataPtr = rawManifest) { EventDescriptorInternal manifestDescr = new EventDescriptorInternal(0xFFFE, 1, 0, 0, 0xFE, 0, -1); ManifestEnvelope envelope = new ManifestEnvelope(); envelope.Format = ManifestEnvelope.ManifestFormats.SimpleXmlFormat; envelope.MajorVersion = 1; envelope.MinorVersion = 0; envelope.Magic = 0x5B; // An unusual number that can be checked for consistancy. int dataLeft = rawManifest.Length; envelope.TotalChunks = (ushort)((dataLeft + (ManifestEnvelope.MaxChunkSize - 1)) / ManifestEnvelope.MaxChunkSize); envelope.ChunkNumber = 0; EventProvider.EventData* dataDescrs = stackalloc EventProvider.EventData[2]; dataDescrs[0].Ptr = (ulong)&envelope; dataDescrs[0].Size = (uint)sizeof(ManifestEnvelope); dataDescrs[0].Reserved = 0; dataDescrs[1].Ptr = (ulong)dataPtr; dataDescrs[1].Reserved = 0; bool success = true; while (dataLeft > 0) { dataDescrs[1].Size = (uint)Math.Min(dataLeft, ManifestEnvelope.MaxChunkSize); #if ETW_SUPPORTED if (outputStream == null && m_provider != null && !m_provider.WriteEvent(ref manifestDescr, 2, (IntPtr)dataDescrs)) success = false; #endif if (outputStream != null) { byte[] envelopeBlob = null; byte[] manifestBlob = null; if (envelopeBlob == null) { envelopeBlob = new byte[dataDescrs[0].Size]; manifestBlob = new byte[dataDescrs[1].Size]; } System.Runtime.InteropServices.Marshal.Copy((IntPtr)dataDescrs[0].Ptr, envelopeBlob, 0, (int)dataDescrs[0].Size); System.Runtime.InteropServices.Marshal.Copy((IntPtr)dataDescrs[1].Ptr, manifestBlob, 0, (int)dataDescrs[1].Size); m_eventCallbackArgs.EventId = manifestDescr.EventId; m_eventCallbackArgs.Payload = new object[] { envelopeBlob, manifestBlob }; outputStream.m_Callback(m_eventCallbackArgs); } dataLeft -= ManifestEnvelope.MaxChunkSize; envelope.ChunkNumber++; } return success; } } [System.Security.SecuritySafeCritical] private byte[] CreateManifestAndDescriptors(string providerDllName) { Type providerType = this.GetType(); MethodInfo[] methods = providerType.GetMethods(BindingFlags.DeclaredOnly | BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance); EventAttribute defaultEventAttribute = new EventAttribute(0); int eventId = 1; // The number given to an event that does not have a explicitly given ID. m_eventData = new EventData[methods.Length]; ManifestBuilder manifest = new ManifestBuilder(Name, Guid, providerDllName); // Collect task, opcode, keyword and channel information FieldInfo[] staticFields = providerType.GetFields(BindingFlags.DeclaredOnly | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static); if (staticFields.Length > 0) { foreach (FieldInfo staticField in staticFields) { Type staticFieldType = staticField.FieldType; if (staticFieldType == typeof(EventOpcode)) manifest.AddOpcode(staticField.Name, (int)staticField.GetRawConstantValue()); else if (staticFieldType == typeof(EventTask)) manifest.AddTask(staticField.Name, (int)staticField.GetRawConstantValue()); else if (staticFieldType == typeof(EventKeywords)) manifest.AddKeyword(staticField.Name, (ulong)(long)staticField.GetRawConstantValue()); else if (staticFieldType == typeof(EventChannel)) manifest.AddChannel(staticField.Name, (int)staticField.GetRawConstantValue()); } } for (int i = 0; i < methods.Length; i++) { MethodInfo method = methods[i]; ParameterInfo[] args = method.GetParameters(); // Get the EventDescriptorInternal (from the Custom attributes) EventAttribute eventAttribute = (EventAttribute)Attribute.GetCustomAttribute(method, typeof(EventAttribute), false); // Methods that don't return void can't be events. if (method.ReturnType != typeof(void)) { if (eventAttribute != null && DoDebugChecks()) throw new ArgumentException("Event attribute placed on method " + method.Name + " which does not return 'void'"); continue; } if (method.IsVirtual || method.IsStatic) { continue; } if (eventAttribute == null) { // If we explictly mark the method as not being an event, then honor that. if (Attribute.GetCustomAttribute(method, typeof(NonEventAttribute), false) != null) continue; defaultEventAttribute.EventId = eventId; defaultEventAttribute.Opcode = EventOpcode.Info; defaultEventAttribute.Task = EventTask.None; eventAttribute = defaultEventAttribute; } else if (eventAttribute.EventId <= 0) throw new ArgumentException("Event IDs <= 0 are illegal."); eventId++; if (eventAttribute.Opcode == EventOpcode.Info && eventAttribute.Task == EventTask.None) eventAttribute.Opcode = (EventOpcode)(10 + eventAttribute.EventId); manifest.StartEvent(method.Name, eventAttribute); for (int fieldIdx = 0; fieldIdx < args.Length; fieldIdx++) manifest.AddEventParameter(args[fieldIdx].ParameterType, args[fieldIdx].Name); manifest.EndEvent(); if (DoDebugChecks()) DebugCheckEvent(method, eventAttribute); AddEventDescriptor(eventAttribute); } TrimEventDescriptors(); m_eventsByName = null; return manifest.CreateManifest(); } [System.Security.SecuritySafeCritical] private void AddEventDescriptor(EventAttribute eventAttribute) { if (m_eventData == null || m_eventData.Length <= eventAttribute.EventId) { EventData[] newValues = new EventData[m_eventData.Length + 16]; Array.Copy(m_eventData, newValues, m_eventData.Length); m_eventData = newValues; } m_eventData[eventAttribute.EventId].Descriptor = new EventDescriptorInternal( eventAttribute.EventId, eventAttribute.Version, (byte)eventAttribute.Channel, (byte)eventAttribute.Level, (byte)eventAttribute.Opcode, (int)eventAttribute.Task, (long)eventAttribute.Keywords); m_eventData[eventAttribute.EventId].CaptureStack = eventAttribute.CaptureStack; m_eventData[eventAttribute.EventId].Message = eventAttribute.Message; } [System.Security.SecuritySafeCritical] private void TrimEventDescriptors() { int idx = m_eventData.Length; while (0 < idx) { --idx; if (m_eventData[idx].Descriptor.EventId != 0) break; } if (m_eventData.Length - idx > 2) // allow one wasted slot. { EventData[] newValues = new EventData[idx + 1]; Array.Copy(m_eventData, newValues, newValues.Length); m_eventData = newValues; } } private void DebugCheckEvent(MethodInfo method, EventAttribute eventAttribute) { int eventArg = GetHelperCallFirstArg(method); if (eventArg >= 0 && eventAttribute.EventId != eventArg) { throw new ArgumentException("Error: event " + method.Name + " is given event ID " + eventAttribute.EventId + " but " + eventArg + " was passed to the helper."); } if (eventAttribute.EventId < m_eventData.Length && m_eventData[eventAttribute.EventId].Descriptor.EventId != 0) { throw new ArgumentException("Event " + method.Name + " has ID " + eventAttribute.EventId + " which is the same as a previously defined event."); } if (m_eventsByName == null) m_eventsByName = new Dictionary (); if (m_eventsByName.ContainsKey(method.Name)) throw new ArgumentException("Event name " + method.Name + " used more than once. " + "If you wish to overload a method, the overloaded method should have a " + "[Event(-1)] attribute to indicate the method should not have associated meta-data."); m_eventsByName[method.Name] = method.Name; } /// /// This method looks at the IL and tries to pattern match against the standard /// 'boilerplate' event body /// /// { if (Enabled()) WriteEvent(#, ...) } /// /// If the pattern matches, it returns the literal number passed as the first parameter to /// the WriteEvent. This is used to find common user errors (mismatching this /// number with the EventAttribute ID). It is only used for validation. /// /// The method to probe. ///The literal value or -1 if the value could not be determined. [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Maintainability", "CA1502:AvoidExcessiveComplexity", Justification = "Switch statement is clearer than alternatives")] static private int GetHelperCallFirstArg(MethodInfo method) { // Currently searches for the following pattern // // ... // CAN ONLY BE THE INSTRUCTIONS BELOW // LDARG0 // LDC.I4 XXX // ... // CAN ONLY BE THE INSTRUCTIONS BELOW CAN'T BE A BRANCH OR A CALL // CALL // NOP // 0 or more times // RET // // If we find this pattern we return the XXX. Otherwise we return -1. byte[] instrs = method.GetMethodBody().GetILAsByteArray(); int retVal = -1; for (int idx = 0; idx < instrs.Length; ) { switch (instrs[idx]) { case 0: // NOP case 1: // BREAK case 2: // LDARG_0 case 3: // LDARG_1 case 4: // LDARG_2 case 5: // LDARG_3 case 6: // LDLOC_0 case 7: // LDLOC_1 case 8: // LDLOC_2 case 9: // LDLOC_3 case 10: // STLOC_0 case 11: // STLOC_1 case 12: // STLOC_2 case 13: // STLOC_3 break; case 14: // LDARG_S case 16: // STARG_S idx++; break; case 20: // LDNULL break; case 21: // LDC_I4_M1 case 22: // LDC_I4_0 case 23: // LDC_I4_1 case 24: // LDC_I4_2 case 25: // LDC_I4_3 case 26: // LDC_I4_4 case 27: // LDC_I4_5 case 28: // LDC_I4_6 case 29: // LDC_I4_7 case 30: // LDC_I4_8 if (idx > 0 && instrs[idx - 1] == 2) // preceeded by LDARG0 retVal = instrs[idx] - 22; break; case 31: // LDC_I4_S if (idx > 0 && instrs[idx - 1] == 2) // preceeded by LDARG0 retVal = instrs[idx + 1]; idx++; break; case 32: // LDC_I4 idx += 4; break; case 37: // DUP break; case 40: // CALL idx += 4; if (retVal >= 0) { // Is this call just before return? for (int search = idx + 1; search < instrs.Length; search++) { if (instrs[search] == 42) // RET return retVal; if (instrs[search] != 0) // NOP break; } } retVal = -1; break; case 44: // BRFALSE_S case 45: // BRTRUE_S retVal = -1; idx++; break; case 57: // BRFALSE case 58: // BRTRUE retVal = -1; idx += 4; break; case 103: // CONV_I1 case 104: // CONV_I2 case 105: // CONV_I4 case 106: // CONV_I8 case 109: // CONV_U4 case 110: // CONV_U8 break; case 140: // BOX case 141: // NEWARR idx += 4; break; case 162: // STELEM_REF break; case 254: // PREFIX idx++; // Covers the CEQ instructions used in debug code for some reason. if (idx >= instrs.Length || instrs[idx] >= 6) goto default; break; default: // Contract.Assert(false, "Warning: User validation code sub-optimial: Unsuported opcode " + instrs[idx] + // " at " + idx + " in method " + method.Name); return -1; } idx++; } return -1; } // private instance state private string m_name; // My friendly name (privided in ctor) internal int m_id; // A small integer that is unique to this instance. private Guid m_guid; // GUID representing the ETW providerBase to the OS. internal EventData[] m_eventData; // All per-event data private byte[] m_rawManifest; // Bytes to send out representing the event schema // Enabling bits private bool m_providerEnabled; // am I enabled (any of my events are enabled) private bool m_ETWManifestSent; // we could not send the ETW manifest as an event in the callback private bool m_completelyInited; // The EventProviderBase constructor has returned. internal EventLevel m_level; // lowest level enabled by any output stream internal EventKeywords m_matchAnyKeyword;// the logical OR of all levels enabled by any output stream. private EventWrittenEventArgs m_eventCallbackArgs; // Passed to the callback (Basically reusing the instance again and again) internal EventProviderDataStream m_OutputStreams; // Linked list of streams we write the data to (we also do ETW specially) #if ETW_SUPPORTED private OverideEventProvider m_provider; // We have special support for ETW. #endif // m_eventsByName is on used for error checking, when DoDebugChecks indicate validation // should be done. In this case m_eventsByName is just a set of events names that // currently exist (value is same as key). Dictionarym_eventsByName; #endregion } /// /// An EventDataStream represents the 'output' stream generated from all subclasses of EventProviderBase in the /// current appdomain. The delegate that was passed to the 'Register' method will be called back each /// time an event is logged if the provider has been turned on for that stream. /// /// Each stream is logically independent from the other streams. Each stream can send commands to the /// providers (using the SendCommand method), and in those commands set filtering criteria. The /// filtering criteria from one stream does not affect other streams. /// /// Logging be turned off by using 'SendCommand'. When the stream is no longer going to be used the 'UnRegister' /// method will disconnect the stream from the event providers. /// internal class EventProviderDataStream : IDisposable { public static EventProviderDataStream Register(ActioncallBack) { lock (EventProviderStreamsLock) { // Add the outputStream to the global list of EventProviderDataStreams. EventProviderDataStream ret = new EventProviderDataStream(callBack, s_Streams, null, null); s_Streams = ret; // Add the outputStream to each existing EventProvider. foreach (WeakReference providerRef in s_providers) { EventProviderBase providerBase = providerRef.Target as EventProviderBase; if (providerBase != null) providerBase.m_OutputStreams = new EventProviderDataStream(callBack, providerBase.m_OutputStreams, new bool[providerBase.EventIdLimit], ret); } return ret; } } public void UnRegister() { lock (EventProviderStreamsLock) { if (s_Streams != null) { if (this == s_Streams) s_Streams = this.m_Next; else { // Remove 'this' from the s_Streams linked list. EventProviderDataStream prev = s_Streams; for (; ; ) { EventProviderDataStream cur = prev.m_Next; if (cur == null) break; if (cur == this) { prev.m_Next = cur.m_Next; // Remove entry. RemoveStreamFromProviders(cur); break; } prev = cur; } } } } } /// /// A small integer (suitable for indexing in an array) identifying this provider. It is unique /// per-appdomain. This allows the callback registered in 'Register' to efficiently attach addition /// information to a provider. /// public static int ProviderId(EventProviderBase provider) { return provider.m_id; } ////// Send a command to a provider (from a particular stream). The most important command is simply /// to turn a particular provider on or off. However particular providers might implement /// additional commands (like dumping particular data structures in response to a command. /// /// The GUID for the event provider that the command is to be sent to. /// Guid.Empty is defined to be a wildcard that means all providers. /// Indicates if the provider is to be turned on or off. The rest of the /// arguments are only appicable if enabled=true. /// The level (verbosity) of the logging desired. /// Providers define groups of events and each such group is given a /// bit in a 64 bit enumeration. This parameter indicates which groups should be truned on. Events /// have are not assigned any group are never filtered by the matchAnyKeyword value. /// The command to be given to the provider. Typically it is Command.Update. /// Individual providers can define their own commands /// This is a set of key-value pairs that are interpreted by the /// provider. It is a way of passing arbitrary arguments to the provider when issuing a command public void SendCommand(Guid providerGuid, bool enable, EventLevel level, EventKeywords matchAnyKeyword, ControllerCommand command, IDictionarycommandArguments) { // Copy it to list so I can make the callback without holding the EventProviderStreamsLock. List providerBases = new List (); lock (EventProviderStreamsLock) { foreach (WeakReference providerRef in s_providers) { EventProviderBase providerBase = providerRef.Target as EventProviderBase; if (providerBase != null && (providerBase.Guid == providerGuid || providerGuid == Guid.Empty)) providerBases.Add(providerBase); } } foreach (EventProviderBase providerBase in providerBases) providerBase.SendCommand(this, enable, level, matchAnyKeyword, command, commandArguments); } public void SendCommand(Guid providerGuid, bool enable, EventLevel level) { SendCommand(providerGuid, enable, level, EventKeywords.None, ControllerCommand.Update, null); } /// /// The code:EventProviderCreated is fired whenever provider (a subclass of /// code:EventProviderBase) is created. Notifications from this event are 'retroactive, /// meaning that when a new handler is added, a callback is immediately issued for all /// providers that were created before the callback was registered. /// public static event EventHandlerEventProviderCreated { add { List providerBases = new List (); lock (EventProviderStreamsLock) { // Add new delegate to the s_EventProviderCreated = (EventHandler )Delegate.Combine(s_EventProviderCreated, value); // Make a copy so I can send events outside the lock foreach (WeakReference providerRef in s_providers) { EventProviderBase providerBase = providerRef.Target as EventProviderBase; if (providerBase != null) providerBases.Add(providerBase); } } // Send the callback for all existing eventsProviders to 'catch up' (outside the lock) foreach (EventProviderBase providerBase in providerBases) value(null, new EventProviderCreatedEventArgs { Provider = providerBase }); } remove { s_EventProviderCreated = (EventHandler )Delegate.Remove(s_EventProviderCreated, value); } } #region private void IDisposable.Dispose() { UnRegister(); } /// /// This routine adds this to the global list of providers, it also assigns the ID to the /// provider (which is simply the oridinal in the global list) /// /// internal static void AddProvider(EventProviderBase newProvider) { lock (EventProviderStreamsLock) { if (s_providers == null) s_providers = new List(2); // Periodically search the list for existing entries to reuse, this avoids // unbounded memory use if we keep recycling providers (an unlikely thing). int newIndex = -1; if (s_providers.Count % 64 == 63) { for (int i = 0; i < s_providers.Count; i++) { WeakReference weakRef = s_providers[i]; if (!weakRef.IsAlive) { newIndex = i; weakRef.Target = newProvider; break; } } } if (newIndex < 0) { newIndex = s_providers.Count; s_providers.Add(new WeakReference(newProvider)); } newProvider.m_id = newIndex; // Add every existing outputStream to the new Provider for (EventProviderDataStream outputStream = s_Streams; outputStream != null; outputStream = outputStream.m_Next) newProvider.m_OutputStreams = new EventProviderDataStream(outputStream.m_Callback, newProvider.m_OutputStreams, new bool[newProvider.EventIdLimit], outputStream); // Put in a local to avoid ---- with null check. EventHandler callback = s_EventProviderCreated; if (callback != null) callback(null, new EventProviderCreatedEventArgs { Provider = newProvider }); } } private void RemoveStreamFromProviders(EventProviderDataStream streamToRemove) { // Foreach existing Provider foreach (WeakReference providerRef in s_providers) { EventProviderBase providerBase = providerRef.Target as EventProviderBase; if (providerBase != null) { // Is the first output outputStream the outputStream we are removing? if (providerBase.m_OutputStreams.m_MasterStream == streamToRemove) providerBase.m_OutputStreams = providerBase.m_OutputStreams.m_Next; else { // Remove 'streamToRemove' from the providerBase.m_OutputStreams linked list. EventProviderDataStream prev = providerBase.m_OutputStreams; for (; ; ) { EventProviderDataStream cur = prev.m_Next; if (cur == null) { Contract.Assert(false, "Provider did not have a registers EventProviderDataStream!"); break; } if (cur.m_MasterStream == streamToRemove) { prev.m_Next = cur.m_Next; // Remove entry. break; } prev = cur; } } } } } // These conditions need to hold most of the time. [Conditional("DEBUG")] static void Validate() { // } internal EventProviderDataStream(Action callBack, EventProviderDataStream next, bool[] eventEnabled, EventProviderDataStream masterStream) { m_Callback = callBack; m_Next = next; m_EventEnabled = eventEnabled; m_MasterStream = masterStream; } internal static object EventProviderStreamsLock { get { if (s_Lock == null) Interlocked.CompareExchange(ref s_Lock, new object(), null); return s_Lock; } } // Instance fields internal Action m_Callback; internal EventProviderDataStream m_Next; // These form a linked list internal bool m_ManifestSent; // Have we sent the manifest? internal EventProviderDataStream m_MasterStream; // If this is a per-Provider outputStream, this is a link // to the outputStream in the code:s_Streams list internal bool[] m_EventEnabled; // For every event in a Provider, // static fields internal static EventProviderDataStream s_Streams; // list of all EventProviderDataStreams in the appdomain internal static object s_Lock; // lock for s_streams; private static List s_providers; // all EventProviders in the appdomain static EventHandler s_EventProviderCreated; // delegate for creating events. #endregion } /// /// code:EventWrittenEventArgs is passed when the callback given in code:EventProviderDataStream.Register is /// fired. /// internal class EventWrittenEventArgs : EventArgs { public int EventId { get; internal set; } public object[] Payload { get; internal set; } public string Message { get { string message = null; if ((uint)EventId < (uint)m_providerBase.m_eventData.Length) message = m_providerBase.m_eventData[EventId].Message; if (message != null) return string.Format(CultureInfo.InvariantCulture, message, Payload); // else if (EventId == 0 && Payload.Length == 1 && Payload[0].GetType() == typeof(string)) // This is for WriteMessage return Payload[0].ToString(); else return null; } } public EventProviderBase Provider { get { return m_providerBase; } } public EventDescriptorInternal Descriptor { get { return m_providerBase.m_eventData[EventId].Descriptor; } } public EventLevel Level { get { if ((uint)EventId >= (uint)m_providerBase.m_eventData.Length) return EventLevel.LogAlways; return (EventLevel)m_providerBase.m_eventData[EventId].Descriptor.Level; } } #region private internal EventWrittenEventArgs(EventProviderBase providerBase) { m_providerBase = providerBase; } private EventProviderBase m_providerBase; #endregion } ////// code:EventProviderCreatedEventArgs is passed when the code:EventProviderDataStream.EventProviderCreated /// is fired. /// internal class EventProviderCreatedEventArgs : EventArgs { public EventProviderBase Provider; } ////// All instance methods in a class that subclasses code:EventProviderBasethat and return void /// are assumed to be methods that generate an event. Enough information can be deduced from the name /// of the method and its signature to generate basic schema information for the event. The /// code:EventAttribute allows you to specify additional event schema information for an event if /// desired. /// [AttributeUsage(AttributeTargets.Method)] [System.Runtime.CompilerServices.FriendAccessAllowed] internal sealed class EventAttribute : Attribute { public EventAttribute(int eventId) { this.EventId = eventId; } public int EventId { get; internal set; } public string Name { get; set; } public EventLevel Level { get; set; } public EventKeywords Keywords { get; set; } public EventOpcode Opcode { get; set; } public EventTask Task { get; set; } public EventChannel Channel { get; set; } public byte Version { get; set; } public bool CaptureStack { get; set; } ////// This is also used for m_OutputStreams compatabilty. If code:EventProviderBase.TraceSourceSupport is /// on events will also be logged a tracesource with the same name as the providerBase. If this /// property is set then the payload will go to code:TraceSource.TraceEvent, and this string /// will be used as the message. if this property is not set not set it goes to /// code:TraceSource.TraceData /// public string Message { get; set; } ////// Like Message, but you specify the resource ID of the string you wish to fetch. It will use the /// ResourceManager to look this resource up in the resources associated with the providerBase type /// public string MessageResourceId { get; set; } } ////// By default all instance methods in a class that subclasses code:EventProviderBasethat and return void /// are assumed to be methods that generate an event. This default can be overriden by specifying the /// code:NonEventAttribute /// [AttributeUsage(AttributeTargets.Method)] [System.Runtime.CompilerServices.FriendAccessAllowed] internal sealed class NonEventAttribute : Attribute { public NonEventAttribute() { } } #region private classes ////// ManifestBuilder is designed to isolate the details of the message of the event from the /// rest of EventProviderBase. This one happens to create XML. /// internal class ManifestBuilder { public ManifestBuilder(string providerName, Guid providerGuid, string dllName) { this.providerGuid = providerGuid; sb = new StringBuilder(); events = new StringBuilder(); templates = new StringBuilder(); opcodeTab = new Dictionary(); sb.Append(" ").AppendLine(); } public void AddOpcode(string name, int value) { opcodeTab[value] = name; } public void AddTask(string name, int value) { if (taskTab == null) taskTab = new Dictionary ").AppendLine(); return Encoding.UTF8.GetBytes(sb.ToString()); } #region private private static string GetLevelName(EventLevel level) { // return "win:" + level.ToString(); } private string GetChannelName(EventChannel channel, string eventName) { string ret = null; if (channelTab == null || !channelTab.TryGetValue((int)channel, out ret)) throw new ArgumentException("Use of undefined channel value " + channel + " for event " + eventName); return ret; } private string GetTaskName(EventTask task, string eventName) { string ret = null; if (taskTab == null || !taskTab.TryGetValue((int)task, out ret)) throw new ArgumentException("Use of undefined task value " + task + " for event " + eventName); return ret; } private string GetOpcodeName(EventOpcode opcode, string eventName) { switch (opcode) { case EventOpcode.Info: return "win:Info"; case EventOpcode.Start: return "win:Start"; case EventOpcode.Stop: return "win:Stop"; case EventOpcode.DataCollectionStart: return "win:DC_Start"; case EventOpcode.DataCollectionStop: return "win:DC_Stop"; case EventOpcode.Extension: return "win:Extension"; case EventOpcode.Reply: return "win:Reply"; case EventOpcode.Resume: return "win:Resume"; case EventOpcode.Suspend: return "win:Suspend"; case EventOpcode.Send: return "win:Send"; case EventOpcode.Receive: return "win:Receive"; } // string ret = null; if (opcodeTab == null) opcodeTab = new Dictionary(); taskTab[value] = name; } public void AddKeyword(string name, ulong value) { if ((value & (value - 1)) != 0) // Is it a power of 2? throw new ArgumentException("Value " + value.ToString("x", CultureInfo.CurrentCulture) + " for keyword " + name + " needs to be a power of 2."); if (keywordTab == null) keywordTab = new Dictionary (); keywordTab[value] = name; } public void AddChannel(string name, int value) { if (channelTab == null) channelTab = new Dictionary (); channelTab[value] = name; } public void StartEvent(string eventName, EventAttribute eventAttribute) { Contract.Assert(numParams == 0); Contract.Assert(templateName == null); templateName = eventName + "Args"; numParams = 0; events.Append(" ").AppendLine(); numParams++; templates.Append(" ").AppendLine(); } public void EndEvent() { if (numParams > 0) { templates.Append(" ").AppendLine(); events.Append(" template=\"").Append(templateName).Append("\""); } events.Append("/>").AppendLine(); templateName = null; numParams = 0; } public byte[] CreateManifest() { // Write out the channels if (channelTab != null) { sb.Append(" ").AppendLine(); foreach (int channel in channelTab.Keys) sb.Append(" ").AppendLine(); } // Write out the tasks if (taskTab != null) { sb.Append("").AppendLine(); sb.Append(" ").AppendLine(); foreach (int task in taskTab.Keys) { Guid taskGuid = EventProvider.GenTaskGuidFromProviderGuid(providerGuid, (ushort)task); sb.Append(" ").AppendLine(); } // Write out the opcodes sb.Append("").AppendLine(); } sb.Append(" ").AppendLine(); foreach (int opcode in opcodeTab.Keys) sb.Append(" ").AppendLine(); // Write out the keywords if (keywordTab != null) { sb.Append("").AppendLine(); sb.Append(" ").AppendLine(); foreach (ulong keyword in keywordTab.Keys) sb.Append(" ").AppendLine(); } sb.Append("").AppendLine(); sb.Append(" ").AppendLine(); sb.Append(events); sb.Append(" ").AppendLine(); if (templates.Length > 0) { sb.Append("").AppendLine(); sb.Append(templates); sb.Append(" ").AppendLine(); } sb.Append("(); if (!opcodeTab.TryGetValue((int)opcode, out ret)) opcodeTab[(int)opcode] = eventName; return eventName; } private string GetKeywords(ulong keywords, string eventName) { string ret = ""; for (ulong bit = 1; bit != 0; bit <<= 1) { if ((keywords & bit) != 0) { string keyword; if (keywordTab == null || !keywordTab.TryGetValue(bit, out keyword)) throw new ArgumentException("Use of undefined keyword value " + bit.ToString("x", CultureInfo.CurrentCulture) + " for event " + eventName); if (ret.Length != 0) ret = ret + " "; ret = ret + keyword; } } return ret; } private static string GetTypeName(Type type) { switch (Type.GetTypeCode(type)) { case TypeCode.Boolean: return "win:Boolean"; case TypeCode.Byte: return "win:Uint8"; case TypeCode.UInt16: return "win:UInt16"; case TypeCode.UInt32: return "win:UInt32"; case TypeCode.UInt64: return "win:UInt64"; case TypeCode.SByte: return "win:Int8"; case TypeCode.Int16: return "win:Int16"; case TypeCode.Int32: return "win:Int32"; case TypeCode.Int64: return "win:Int64"; case TypeCode.String: return "win:UnicodeString"; case TypeCode.Single: return "win:Float"; case TypeCode.Double: return "win:Double"; default: if (type == typeof(Guid)) return "win:GUID"; throw new ArgumentException("Unsupported type " + type.Name); } } Dictionary opcodeTab; Dictionary taskTab; Dictionary channelTab; Dictionary keywordTab; StringBuilder sb; StringBuilder events; StringBuilder templates; Guid providerGuid; string templateName; int numParams; #endregion } /// /// Used to send the m_rawManifest into the event outputStream as a series of events. /// internal struct ManifestEnvelope { public const int MaxChunkSize = 0xFF00; public enum ManifestFormats : byte { SimpleXmlFormat = 1, // Simply dump what is under thetag in an XML manifest } public ManifestFormats Format; public byte MajorVersion; public byte MinorVersion; public byte Magic; public ushort TotalChunks; public ushort ChunkNumber; }; #endregion } // File provided for Reference Use Only by Microsoft Corporation (c) 2007.
Link Menu

This book is available now!
Buy at Amazon US or
Buy at Amazon UK
- XmlDataSourceView.cs
- Debug.cs
- DataGridViewSortCompareEventArgs.cs
- DeclarationUpdate.cs
- ControlEvent.cs
- MergeFailedEvent.cs
- ReferenceConverter.cs
- SmtpClient.cs
- SqlDataSourceSelectingEventArgs.cs
- RowUpdatedEventArgs.cs
- DataGridViewCellStateChangedEventArgs.cs
- SizeAnimationUsingKeyFrames.cs
- SelectedDatesCollection.cs
- PolicyVersion.cs
- WsdlInspector.cs
- _NegoState.cs
- TemplateKey.cs
- NavigationWindowAutomationPeer.cs
- WindowsGraphics.cs
- WebConfigurationHostFileChange.cs
- PerformanceCounterTraceRecord.cs
- TemplateKeyConverter.cs
- DateTimeFormat.cs
- XmlWrappingReader.cs
- SoapAttributes.cs
- HtmlInputControl.cs
- RegistrySecurity.cs
- OrthographicCamera.cs
- BooleanSwitch.cs
- DataGridViewSelectedRowCollection.cs
- JsonStringDataContract.cs
- TaskbarItemInfo.cs
- DataGridViewComboBoxCell.cs
- DataGridViewRow.cs
- ProgressBarAutomationPeer.cs
- HttpServerUtilityWrapper.cs
- PeerEndPoint.cs
- SimpleColumnProvider.cs
- ToolboxItemAttribute.cs
- HtmlShim.cs
- RootDesignerSerializerAttribute.cs
- ClientSponsor.cs
- CodeCompiler.cs
- TransportSecurityBindingElement.cs
- CanonicalXml.cs
- ReferentialConstraint.cs
- UriParserTemplates.cs
- MessageLoggingElement.cs
- ReferentialConstraint.cs
- AttributeProviderAttribute.cs
- SafeViewOfFileHandle.cs
- EdmSchemaError.cs
- VariableQuery.cs
- ExternalDataExchangeService.cs
- HttpContextServiceHost.cs
- XamlVector3DCollectionSerializer.cs
- SqlSelectClauseBuilder.cs
- Pens.cs
- CodePropertyReferenceExpression.cs
- DateTimeFormatInfo.cs
- CodeChecksumPragma.cs
- APCustomTypeDescriptor.cs
- Duration.cs
- ISO2022Encoding.cs
- DbProviderFactories.cs
- ToolStripSystemRenderer.cs
- PropertyTabAttribute.cs
- GridToolTip.cs
- ImageMetadata.cs
- Floater.cs
- Trace.cs
- SortQuery.cs
- PagerStyle.cs
- DataGridViewCheckBoxCell.cs
- PropertyGroupDescription.cs
- HttpMethodConstraint.cs
- SignedPkcs7.cs
- SeparatorAutomationPeer.cs
- ThreadAbortException.cs
- IdentityValidationException.cs
- PolyQuadraticBezierSegmentFigureLogic.cs
- DataChangedEventManager.cs
- Version.cs
- DataGridViewBindingCompleteEventArgs.cs
- DataSourceDescriptorCollection.cs
- LineBreakRecord.cs
- _OverlappedAsyncResult.cs
- DocumentSequenceHighlightLayer.cs
- MimeWriter.cs
- ApplicationId.cs
- RequestQueue.cs
- TabPanel.cs
- IItemContainerGenerator.cs
- HtmlSelect.cs
- ReferenceEqualityComparer.cs
- TextDecorations.cs
- PrivateUnsafeNativeCompoundFileMethods.cs
- ObjectItemCachedAssemblyLoader.cs
- SaveFileDialog.cs
- SafeRightsManagementPubHandle.cs