Skip to content

EventPipe doesn't correctly recompute global keyword and level filters after session close #121462

@noahfalk

Description

@noahfalk

Description

EventPipe is intended to maintain a current keyword and level filter for each EventSource based on the set of active tracing sessions. Whenever a new session starts or an existing session finishes these filters may need to be updated. Unforetunately the update on session end isn't correct and the keyword and level filters still include the session that is exiting. One of the ways the keyword and level filters are visible is through the EventSource.IsEnabled(level, keyword) API which returns incorrect results.

Reproduction Steps

  1. Compile and run this code:
using System.Diagnostics.Tracing;

namespace ConsoleApp63
{
    internal class Program
    {
        static void Main(string[] args)
        {
            MyEventSource.Log.Info1("Hello, World!");
            Console.ReadLine();
        }
    }

    [EventSource(Name ="MyEventSource")]
    public class MyEventSource : EventSource
    {
        public static MyEventSource Log = new MyEventSource();
        [Event(1, Level = EventLevel.Informational)]
        public void Info1(string message)
        {
            WriteEvent(1, message);
        }

        override protected void OnEventCommand(EventCommandEventArgs args)
        {
            Console.WriteLine($"IsEnabled(Level=Info,Keyword=2): {this.IsEnabled(EventLevel.Informational, (EventKeywords)0x2)}");
        }
    }
}
  1. In a 2nd console window start an EventPipe session by running:
    dotnet-trace collect -n <app_name_here> --providers MyEventSource:1:Error

  2. In a 3rd console window start another EventPipe session by running:
    dotnet-trace collect -n <app_name_here> --providers MyEventSource:2:Informational

  3. Hit enter in the 3rd console window to stop the Informational level session

  4. Hit enter in the 2nd console window to stop the Error level session

Expected behavior

Each step 2-5 should print one line of this output:

IsEnabled(Level=Info,Keyword=2): False
IsEnabled(Level=Info,Keyword=2): True
IsEnabled(Level=Info,Keyword=2): False
IsEnabled(Level=Info,Keyword=2): False

Actual behavior

Each step 2-5 should print one line of this output:

IsEnabled(Level=Info,Keyword=2): False
IsEnabled(Level=Info,Keyword=2): True
IsEnabled(Level=Info,Keyword=2): True
IsEnabled(Level=Info,Keyword=2): False

Regression?

Unverified, but the issue probably extends back to when EventPipe was first added.

Known Workarounds

No response

Configuration

Windows x64 .NET 10 Preview but I expect it reproes on any config

Other information

I believe there are actually two separate bugs that cause this:

  1. In the EventPipe source code when disabling a session the update calcuation for level and keyword should exclude the session being disabled but doesn't:
    https://github.com/dotnet/runtime/blob/main/src/native/eventpipe/ep-config.c#L588

  2. In the EventSource code when a session disables a provider the keyword and level aren't recalculated

    if (!commandArgs.enable)
    {
    // If we are disabling, maybe we can turn on 'quick checks' to filter
    // quickly. These are all just optimizations (since later checks will still filter)
    // There is a good chance EnabledForAnyListener are not as accurate as
    // they could be, go ahead and get a better estimate.
    foreach (int eventID in m_eventData.Keys)
    {
    bool isEnabledForAnyListener = false;
    for (EventDispatcher? dispatcher = m_Dispatchers; dispatcher != null; dispatcher = dispatcher.m_Next)
    {
    Debug.Assert(dispatcher.m_EventEnabled != null);
    if (dispatcher.m_EventEnabled[eventID])
    {
    isEnabledForAnyListener = true;
    break;
    }
    }
    ref EventMetadata eventMeta = ref CollectionsMarshal.GetValueRefOrNullRef(m_eventData, eventID);
    eventMeta.EnabledForAnyListener = isEnabledForAnyListener;
    }
    // If no events are enabled, disable the global enabled bit.
    if (!AnyEventEnabled())
    {
    m_level = 0;
    m_matchAnyKeyword = 0;
    m_eventSourceEnabled = false;
    }
    }

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions