#if !UNIX // // Copyright (C) Microsoft. All rights reserved. // using System.Collections.Generic; using System.Diagnostics.Eventing; using System.Diagnostics; using System.Runtime.InteropServices; using System.Diagnostics.CodeAnalysis; namespace System.Management.Automation.Tracing { /// /// Attribute to represent an EtwEvent /// [AttributeUsage(AttributeTargets.Method)] [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly")] [SuppressMessage("Microsoft.Naming", "CA1710:IdentifiersShouldHaveCorrectSuffix")] public sealed class EtwEvent : Attribute { /// /// Constructor /// /// public EtwEvent(long eventId) { this.EventId = eventId; } /// /// EventId /// public long EventId { get; } } /// /// Delegates that defines a call back with no parameter /// public delegate void CallbackNoParameter(); /// /// Delegates that defines a call back with one parameter (state) /// public delegate void CallbackWithState(object state); /// /// Delegates that defines a call back with two parameters; state and ElapsedEventArgs. /// It will be used in System.Timers.Timer scenarios. /// public delegate void CallbackWithStateAndArgs(object state, System.Timers.ElapsedEventArgs args); /// /// ETW events argument class /// [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly")] public class EtwEventArgs : EventArgs { /// Gets Event descriptor public EventDescriptor Descriptor { get; private set; } /// Gets whether the event is successfully written public bool Success { get; private set; } /// Gets payload in the event [SuppressMessage("Microsoft.Performance", "CA1819:PropertiesShouldNotReturnArrays")] public object[] Payload { get; private set; } /// /// Creates a new instance of EtwEventArgs class. /// /// Event descriptor /// Indicate whether the event is successfully written /// Event payload public EtwEventArgs(EventDescriptor descriptor, bool success, object[] payload) { this.Descriptor = descriptor; this.Payload = payload; this.Success = success; } } /// /// This the abstract base class of all activity classes that represent an end-to-end scenario. /// [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly")] public abstract class EtwActivity { /// /// This is a helper class that is used to wrap many multi-threading scenarios /// and makes correlation event to be logged easily. /// private class CorrelatedCallback { private CallbackNoParameter callbackNoParam; private CallbackWithState callbackWithState; private AsyncCallback asyncCallback; /// /// parentActivityId /// protected readonly Guid parentActivityId; private readonly EtwActivity tracer; /// /// EtwCorrelator Constructor /// /// /// public CorrelatedCallback(EtwActivity tracer, CallbackNoParameter callback) { if (callback == null) { throw new ArgumentNullException("callback"); } if (tracer == null) { throw new ArgumentNullException("tracer"); } this.tracer = tracer; this.parentActivityId = EtwActivity.GetActivityId(); this.callbackNoParam = callback; } /// /// EtwCorrelator Constructor /// /// /// public CorrelatedCallback(EtwActivity tracer, CallbackWithState callback) { if (callback == null) { throw new ArgumentNullException("callback"); } if (tracer == null) { throw new ArgumentNullException("tracer"); } this.tracer = tracer; this.parentActivityId = EtwActivity.GetActivityId(); this.callbackWithState = callback; } /// /// EtwCorrelator Constructor /// /// /// public CorrelatedCallback(EtwActivity tracer, AsyncCallback callback) { if (callback == null) { throw new ArgumentNullException("callback"); } if (tracer == null) { throw new ArgumentNullException("tracer"); } this.tracer = tracer; this.parentActivityId = EtwActivity.GetActivityId(); this.asyncCallback = callback; } /// /// It is to be used in System.Timers.Timer scenarios. /// private CallbackWithStateAndArgs callbackWithStateAndArgs; /// /// EtwCorrelator Constructor /// /// /// public CorrelatedCallback(EtwActivity tracer, CallbackWithStateAndArgs callback) { if (callback == null) { throw new ArgumentNullException("callback"); } if (tracer == null) { throw new ArgumentNullException("tracer"); } this.tracer = tracer; this.parentActivityId = EtwActivity.GetActivityId(); this.callbackWithStateAndArgs = callback; } /// /// This is the wrapper on the actual callback /// public void Callback(object state, System.Timers.ElapsedEventArgs args) { Debug.Assert(callbackWithStateAndArgs != null, "callback is NULL. There MUST always ba a valid callback!"); Correlate(); this.callbackWithStateAndArgs(state, args); } /// /// Correlate /// private void Correlate() { tracer.CorrelateWithActivity(this.parentActivityId); } /// /// This is the wrapper on the actual callback /// public void Callback() { Debug.Assert(callbackNoParam != null, "callback is NULL. There MUST always ba a valid callback"); Correlate(); this.callbackNoParam(); } /// /// This is the wrapper on the actual callback /// public void Callback(object state) { Debug.Assert(callbackWithState != null, "callback is NULL. There MUST always ba a valid callback!"); Correlate(); this.callbackWithState(state); } /// /// This is the wrapper on the actual callback /// public void Callback(IAsyncResult asyncResult) { Debug.Assert(asyncCallback != null, "callback is NULL. There MUST always ba a valid callback!"); Correlate(); this.asyncCallback(asyncResult); } } private static Guid powerShellProviderId = Guid.Parse("A0C1853B-5C40-4b15-8766-3CF1C58F985A"); private static Dictionary providers = new Dictionary(); private static object syncLock = new object(); private static EventDescriptor _WriteTransferEvent = new EventDescriptor(0x1f05, 0x1, 0x11, 0x5, 0x14, 0x0, (long)0x4000000000000000); private EventProvider currentProvider; /// /// Event handler for the class /// public static event EventHandler EventWritten; static EtwActivity() { } /// /// Sets the activityId provided in the current thread. /// If current thread already has the same activityId it does /// nothing. /// /// /// true when provided activity was set, false if current activity /// was found to be same and set was not needed public static bool SetActivityId(Guid activityId) { if (GetActivityId() != activityId) { EventProvider.SetActivityId(ref activityId); return true; } return false; } /// /// Creates a new ActivityId that can be used to set in the thread's context. /// /// public static Guid CreateActivityId() { return EventProvider.CreateActivityId(); } /// /// Returns the ActivityId set in current thread /// /// [SuppressMessage("Microsoft.Usage", "CA1806:DoNotIgnoreMethodResults")] public static Guid GetActivityId() { Guid activityId = Guid.Empty; uint hresult = UnsafeNativeMethods.EventActivityIdControl(UnsafeNativeMethods.ActivityControlCode.Get, ref activityId); return activityId; } /// /// Constructor /// protected EtwActivity() { } /// /// CorrelateWithActivity (EventId: 0x1f05/7941) /// This method also sets a new activity id in current thread. /// And then correlates the new id with parentActivityId /// public void CorrelateWithActivity(Guid parentActivityId) { EventProvider provider = GetProvider(); if (!provider.IsEnabled()) return; Guid activityId = CreateActivityId(); SetActivityId(activityId); if (parentActivityId != Guid.Empty) { EventDescriptor transferEvent = TransferEvent; provider.WriteTransferEvent(ref transferEvent, parentActivityId, activityId, parentActivityId); } } /// /// IsEnabled /// public bool IsEnabled { get { return GetProvider().IsEnabled(); } } /// /// Checks whether a provider matching certain levels and keyword is enabled /// /// Levels to check /// Keywords to check /// True, if any ETW listener is enabled else false public bool IsProviderEnabled(byte levels, long keywords) { return GetProvider().IsEnabled(levels, keywords); } /// /// Correlates parent activity id set in the thread with a new activity id /// If parent activity id is not, it just sets a new activity in the current thread. And does not write the Transfer event /// public void Correlate() { Guid parentActivity = GetActivityId(); CorrelateWithActivity(parentActivity); } /// /// Wraps a callback with no params /// /// /// public CallbackNoParameter Correlate(CallbackNoParameter callback) { if (callback == null) { throw new ArgumentNullException("callback"); } return new CorrelatedCallback(this, callback).Callback; } /// /// Wraps a callback with one object param /// /// /// public CallbackWithState Correlate(CallbackWithState callback) { if (callback == null) { throw new ArgumentNullException("callback"); } return new CorrelatedCallback(this, callback).Callback; } /// /// Wraps a AsyncCallback with IAsyncResult param /// /// /// public AsyncCallback Correlate(AsyncCallback callback) { if (callback == null) { throw new ArgumentNullException("callback"); } return new CorrelatedCallback(this, callback).Callback; } /// /// Wraps a callback with one object param and one ElapsedEventArgs object /// This is menat to be used in System.Timers.Timer scenarios. /// /// /// public CallbackWithStateAndArgs Correlate(CallbackWithStateAndArgs callback) { if (callback == null) { throw new ArgumentNullException("callback"); } return new CorrelatedCallback(this, callback).Callback; } /// /// The provider where the tracing messages will be written to. /// protected virtual Guid ProviderId { get { return powerShellProviderId; } } /// /// The event that is defined to be used to log transfer event. /// The derived class must override this property if they don't /// want to use the PowerShell's transfer event. /// protected virtual EventDescriptor TransferEvent { get { return _WriteTransferEvent; } } /// /// This is the main method that write the messages to the trace. /// All derived classes must use this method to write to the provider log. /// /// EventDescriptor /// payload protected void WriteEvent(EventDescriptor ed, params object[] payload) { EventProvider provider = GetProvider(); if (!provider.IsEnabled()) return; if (payload != null) { for (int i = 0; i < payload.Length; i++) { if (payload[i] == null) { payload[i] = string.Empty; } } } bool success = provider.WriteEvent(ref ed, payload); if (EventWritten != null) { EventWritten.Invoke(this, new EtwEventArgs(ed, success, payload)); } } private EventProvider GetProvider() { if (currentProvider != null) return currentProvider; lock (syncLock) { if (currentProvider != null) return currentProvider; if (!providers.TryGetValue(ProviderId, out currentProvider)) { currentProvider = new EventProvider(ProviderId); providers[ProviderId] = currentProvider; } } return currentProvider; } private static class UnsafeNativeMethods { internal enum ActivityControlCode : uint { /// /// Gets the ActivityId from thread local storage. /// Get = 1, /// /// Sets the ActivityId in the thread local storage. /// Set = 2, /// /// Creates a new activity id /// Create = 3, /// /// Sets the activity id in thread local storage and returns the previous value. /// GetSet = 4, /// /// Creates a new activity id, sets thread local storage, and returns the previous value. /// CreateSet = 5 } /// /// Provides interop access to creating, querying and setting the current activity identifier /// /// The indicating the type of operation to perform. /// The activity id to set or retrieve. /// Zero on success. [DllImport(PinvokeDllNames.EventActivityIdControlDllName, ExactSpelling = true, EntryPoint = "EventActivityIdControl", CharSet = CharSet.Unicode)] internal static extern unsafe uint EventActivityIdControl([In] ActivityControlCode controlCode, [In][Out] ref Guid activityId); } } } #endif