File: src\libraries\System.Private.CoreLib\src\System\Diagnostics\Tracing\EventPipeEventProvider.cs
Web Access
Project: src\src\coreclr\System.Private.CoreLib\System.Private.CoreLib.csproj (System.Private.CoreLib)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
 
using System.Collections.Generic;
using System.Runtime.InteropServices;
 
namespace System.Diagnostics.Tracing
{
    internal sealed class EventPipeEventProvider : EventProviderImpl
    {
        private readonly WeakReference<EventProvider> _eventProvider;
        private IntPtr _provHandle;
        private GCHandle _gcHandle;
 
        internal EventPipeEventProvider(EventProvider eventProvider)
        {
            _eventProvider = new WeakReference<EventProvider>(eventProvider);
        }
 
        protected override unsafe void HandleEnableNotification(
                                    EventProvider target,
                                    byte* additionalData,
                                    byte level,
                                    long matchAnyKeywords,
                                    long matchAllKeywords,
                                    Interop.Advapi32.EVENT_FILTER_DESCRIPTOR* filterData)
        {
            ulong id = 0;
            if (additionalData != null)
            {
                id = BitConverter.ToUInt64(new ReadOnlySpan<byte>(additionalData, sizeof(ulong)));
            }
 
            // EventPipe issues Interop.Advapi32.EVENT_CONTROL_CODE_ENABLE_PROVIDER if a session
            // is stopping as long as some other session is still enabled. If the session is stopping
            // the session ID will be null, if it is a session starting it will be a non-zero value
            bool bEnabling = id != 0;
 
            IDictionary<string, string?>? args = null;
            ControllerCommand command = ControllerCommand.Update;
 
            if (bEnabling)
            {
                byte[]? filterDataBytes = null;
                if (filterData != null)
                {
                    MarshalFilterData(filterData, out command, out filterDataBytes);
                }
 
                args = ParseFilterData(filterDataBytes);
            }
 
            // Since we are sharing logic across ETW and EventPipe in OnControllerCommand we have to set up data to
            // mimic ETW to get the right commands sent to EventSources. perEventSourceSessionId has special meaning,
            // if it is -1 the this command will be translated to a Disable command in EventSource.OnEventCommand. If
            // it is 0-3 it indicates an ETW session with activities, and SessionMask.MAX (4) means legacy ETW session.
            // We send SessionMask.MAX just to conform.
            target.OnControllerCommand(command, args, bEnabling ? (int)SessionMask.MAX : -1);
        }
 
        [UnmanagedCallersOnly]
        private static unsafe void Callback(byte* sourceId, int isEnabled, byte level,
            long matchAnyKeywords, long matchAllKeywords, Interop.Advapi32.EVENT_FILTER_DESCRIPTOR* filterData, void* callbackContext)
        {
            EventPipeEventProvider _this = (EventPipeEventProvider)GCHandle.FromIntPtr((IntPtr)callbackContext).Target!;
            if (_this._eventProvider.TryGetTarget(out EventProvider? target))
            {
                _this.ProviderCallback(target, sourceId, isEnabled, level, matchAnyKeywords, matchAllKeywords, filterData);
            }
        }
 
        // Register an event provider.
        internal override unsafe void Register(EventSource eventSource)
        {
            Debug.Assert(!_gcHandle.IsAllocated);
            _gcHandle = GCHandle.Alloc(this);
 
            _provHandle = EventPipeInternal.CreateProvider(eventSource.Name, &Callback, (void*)GCHandle.ToIntPtr(_gcHandle));
            if (_provHandle == 0)
            {
                // Unable to create the provider.
                _gcHandle.Free();
                throw new OutOfMemoryException();
            }
        }
 
        // Unregister an event provider.
        internal override void Unregister()
        {
            if (_provHandle != 0)
            {
                EventPipeInternal.DeleteProvider(_provHandle);
                _provHandle = 0;
            }
            if (_gcHandle.IsAllocated)
            {
                _gcHandle.Free();
            }
        }
 
        // Write an event.
        internal override unsafe EventProvider.WriteEventErrorCode EventWriteTransfer(
            in EventDescriptor eventDescriptor,
            IntPtr eventHandle,
            Guid* activityId,
            Guid* relatedActivityId,
            int userDataCount,
            EventProvider.EventData* userData)
        {
            if (eventHandle != IntPtr.Zero)
            {
                if (userDataCount == 0)
                {
                    EventPipeInternal.WriteEventData(eventHandle, null, 0, activityId, relatedActivityId);
                    return EventProvider.WriteEventErrorCode.NoError;
                }
 
                // If Channel == 11, this is a TraceLogging event.
                // The first 3 descriptors contain event metadata that is emitted for ETW and should be discarded on EventPipe.
                // EventPipe metadata is provided via the EventPipeEventProvider.DefineEventHandle.
                if (eventDescriptor.Channel == 11)
                {
                    userData += 3;
                    userDataCount -= 3;
                    Debug.Assert(userDataCount >= 0);
                }
                EventPipeInternal.WriteEventData(eventHandle, userData, (uint)userDataCount, activityId, relatedActivityId);
            }
 
            return EventProvider.WriteEventErrorCode.NoError;
        }
 
        // Get or set the per-thread activity ID.
        internal override int ActivityIdControl(Interop.Advapi32.ActivityControl controlCode, ref Guid activityId)
        {
            return EventActivityIdControl(controlCode, ref activityId);
        }
 
        // Define an EventPipeEvent handle.
        internal override unsafe IntPtr DefineEventHandle(uint eventID, string eventName, long keywords, uint eventVersion, uint level,
            byte* pMetadata, uint metadataLength)
        {
            return EventPipeInternal.DefineEvent(_provHandle, eventID, keywords, eventVersion, level, pMetadata, metadataLength);
        }
 
        // Get or set the per-thread activity ID.
        internal static int EventActivityIdControl(Interop.Advapi32.ActivityControl controlCode, ref Guid activityId)
        {
            return EventPipeInternal.EventActivityIdControl((uint)controlCode, ref activityId);
        }
    }
}