/
githubmirror
/
PowerShell
Обзор
Документация
Войти
/
githubmirror
/
PowerShell
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
master
src/System.Management.Automation/utils/tracing/EtwActivity.cs
465 строк
15 KB
Dongbo Wang
Remove the use of Windows PowerShell ETW provider id and update `PSDiagnostics` module to work for PowerShell 7 (#25590)
01 окт 2025, 22:38
Не верифицирован
01 окт 2025, 22:38
b852378
Код
Авторство
О чём код?
// Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #if !UNIX using System.Collections.Generic; using System.Diagnostics.Eventing; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; namespace System.Management.Automation.Tracing { /// <summary> /// Attribute to represent an EtwEvent. /// </summary> [AttributeUsage(AttributeTargets.Method)] [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly")] [SuppressMessage("Microsoft.Naming", "CA1710:IdentifiersShouldHaveCorrectSuffix")] public sealed class EtwEvent : Attribute { /// <summary> /// Constructor. /// </summary> /// <param name="eventId"></param> public EtwEvent(long eventId) { this.EventId = eventId; } /// <summary> /// EventId. /// </summary> public long EventId { get; } } /// <summary> /// Delegates that defines a call back with no parameter. /// </summary> public delegate void CallbackNoParameter(); /// <summary> /// Delegates that defines a call back with one parameter (state) /// </summary> public delegate void CallbackWithState(object state); /// <summary> /// Delegates that defines a call back with two parameters; state and ElapsedEventArgs. /// It will be used in System.Timers.Timer scenarios. /// </summary> public delegate void CallbackWithStateAndArgs(object state, System.Timers.ElapsedEventArgs args); /// <summary> /// ETW events argument class. /// </summary> [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly")] public class EtwEventArgs : EventArgs { /// <summary> Gets Event descriptor </summary> public EventDescriptor Descriptor { get; private set; } /// <summary> Gets whether the event is successfully written </summary> public bool Success { get; } /// <summary> Gets payload in the event </summary> [SuppressMessage("Microsoft.Performance", "CA1819:PropertiesShouldNotReturnArrays")] public object[] Payload { get; } /// <summary> /// Creates a new instance of EtwEventArgs class. /// </summary> /// <param name="descriptor">Event descriptor.</param> /// <param name="success">Indicate whether the event is successfully written.</param> /// <param name="payload">Event payload.</param> public EtwEventArgs(EventDescriptor descriptor, bool success, object[] payload) { this.Descriptor = descriptor; this.Payload = payload; this.Success = success; } } /// <summary> /// This the abstract base class of all activity classes that represent an end-to-end scenario. /// </summary> [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly")] public abstract class EtwActivity { /// <summary> /// This is a helper class that is used to wrap many multi-threading scenarios /// and makes correlation event to be logged easily. /// </summary> private sealed class CorrelatedCallback { private readonly CallbackNoParameter callbackNoParam; private readonly CallbackWithState callbackWithState; private readonly AsyncCallback asyncCallback; /// <summary> /// ParentActivityId. /// </summary> private readonly Guid parentActivityId; private readonly EtwActivity tracer; /// <summary> /// EtwCorrelator Constructor. /// </summary> /// <param name="tracer"></param> /// <param name="callback"></param> public CorrelatedCallback(EtwActivity tracer, CallbackNoParameter callback) { ArgumentNullException.ThrowIfNull(callback); ArgumentNullException.ThrowIfNull(tracer); this.tracer = tracer; this.parentActivityId = EtwActivity.GetActivityId(); this.callbackNoParam = callback; } /// <summary> /// EtwCorrelator Constructor. /// </summary> /// <param name="tracer"></param> /// <param name="callback"></param> public CorrelatedCallback(EtwActivity tracer, CallbackWithState callback) { ArgumentNullException.ThrowIfNull(callback); ArgumentNullException.ThrowIfNull(tracer); this.tracer = tracer; this.parentActivityId = EtwActivity.GetActivityId(); this.callbackWithState = callback; } /// <summary> /// EtwCorrelator Constructor. /// </summary> /// <param name="tracer"></param> /// <param name="callback"></param> public CorrelatedCallback(EtwActivity tracer, AsyncCallback callback) { ArgumentNullException.ThrowIfNull(callback); ArgumentNullException.ThrowIfNull(tracer); this.tracer = tracer; this.parentActivityId = EtwActivity.GetActivityId(); this.asyncCallback = callback; } /// <summary> /// It is to be used in System.Timers.Timer scenarios. /// </summary> private readonly CallbackWithStateAndArgs callbackWithStateAndArgs; /// <summary> /// EtwCorrelator Constructor. /// </summary> /// <param name="tracer"></param> /// <param name="callback"></param> public CorrelatedCallback(EtwActivity tracer, CallbackWithStateAndArgs callback) { ArgumentNullException.ThrowIfNull(callback); ArgumentNullException.ThrowIfNull(tracer); this.tracer = tracer; this.parentActivityId = EtwActivity.GetActivityId(); this.callbackWithStateAndArgs = callback; } /// <summary> /// This is the wrapper on the actual callback. /// </summary> 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); } /// <summary> /// Correlate. /// </summary> private void Correlate() { tracer.CorrelateWithActivity(this.parentActivityId); } /// <summary> /// This is the wrapper on the actual callback. /// </summary> public void Callback() { Debug.Assert(callbackNoParam != null, "callback is NULL. There MUST always ba a valid callback"); Correlate(); this.callbackNoParam(); } /// <summary> /// This is the wrapper on the actual callback. /// </summary> public void Callback(object state) { Debug.Assert(callbackWithState != null, "callback is NULL. There MUST always ba a valid callback!"); Correlate(); this.callbackWithState(state); } /// <summary> /// This is the wrapper on the actual callback. /// </summary> 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 readonly Dictionary<Guid, EventProvider> providers = new Dictionary<Guid, EventProvider>(); private static readonly object syncLock = new object(); private static readonly EventDescriptor _WriteTransferEvent = new EventDescriptor(0x1f05, 0x1, 0x11, 0x5, 0x14, 0x0, (long)0x4000000000000000); private EventProvider currentProvider; /// <summary> /// Event handler for the class. /// </summary> public static event EventHandler<EtwEventArgs> EventWritten; /// <summary> /// Sets the activityId provided in the current thread. /// If current thread already has the same activityId it does /// nothing. /// </summary> /// <param name="activityId"></param> /// <returns>True when provided activity was set, false if current activity /// was found to be same and set was not needed.</returns> public static bool SetActivityId(Guid activityId) { if (GetActivityId() != activityId) { EventProvider.SetActivityId(ref activityId); return true; } return false; } /// <summary> /// Creates a new ActivityId that can be used to set in the thread's context. /// </summary> /// <returns></returns> public static Guid CreateActivityId() { return EventProvider.CreateActivityId(); } /// <summary> /// Returns the ActivityId set in current thread. /// </summary> /// <returns></returns> [SuppressMessage("Microsoft.Usage", "CA1806:DoNotIgnoreMethodResults")] public static Guid GetActivityId() { Guid activityId = Guid.Empty; Interop.Windows.GetEventActivityIdControl(ref activityId); return activityId; } /// <summary> /// Constructor. /// </summary> protected EtwActivity() { } /// <summary> /// CorrelateWithActivity (EventId: 0x1f05/7941) /// This method also sets a new activity id in current thread. /// And then correlates the new id with parentActivityId. /// </summary> 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(in transferEvent, parentActivityId, activityId, parentActivityId); } } /// <summary> /// IsEnabled. /// </summary> public bool IsEnabled { get { return GetProvider().IsEnabled(); } } /// <summary> /// Checks whether a provider matching certain levels and keyword is enabled. /// </summary> /// <param name="levels">Levels to check.</param> /// <param name="keywords">Keywords to check.</param> /// <returns>True, if any ETW listener is enabled else false.</returns> public bool IsProviderEnabled(byte levels, long keywords) { return GetProvider().IsEnabled(levels, keywords); } /// <summary> /// 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. /// </summary> public void Correlate() { Guid parentActivity = GetActivityId(); CorrelateWithActivity(parentActivity); } /// <summary> /// Wraps a callback with no params. /// </summary> /// <param name="callback"></param> /// <returns></returns> public CallbackNoParameter Correlate(CallbackNoParameter callback) { ArgumentNullException.ThrowIfNull(callback); return new CorrelatedCallback(this, callback).Callback; } /// <summary> /// Wraps a callback with one object param. /// </summary> /// <param name="callback"></param> /// <returns></returns> public CallbackWithState Correlate(CallbackWithState callback) { ArgumentNullException.ThrowIfNull(callback); return new CorrelatedCallback(this, callback).Callback; } /// <summary> /// Wraps a AsyncCallback with IAsyncResult param. /// </summary> /// <param name="callback"></param> /// <returns></returns> public AsyncCallback Correlate(AsyncCallback callback) { ArgumentNullException.ThrowIfNull(callback); return new CorrelatedCallback(this, callback).Callback; } /// <summary> /// Wraps a callback with one object param and one ElapsedEventArgs object /// This is menat to be used in System.Timers.Timer scenarios. /// </summary> /// <param name="callback"></param> /// <returns></returns> public CallbackWithStateAndArgs Correlate(CallbackWithStateAndArgs callback) { ArgumentNullException.ThrowIfNull(callback); return new CorrelatedCallback(this, callback).Callback; } /// <summary> /// The provider where the tracing messages will be written to. /// </summary> protected virtual Guid ProviderId { get { return PSEtwLogProvider.ProviderGuid; } } /// <summary> /// 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. /// </summary> protected virtual EventDescriptor TransferEvent { get { return _WriteTransferEvent; } } /// <summary> /// 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. /// </summary> /// <param name="ed">EventDescriptor.</param> /// <param name="payload">Payload.</param> 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(in ed, payload); 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; } } } #endif