Skip to content

Extend PredefinedDynamicTraceEventParser to Support Dynamic Events from EventPipeEventSource #2232

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 9 commits into
base: main
Choose a base branch
from

Conversation

Copilot
Copy link
Contributor

@Copilot Copilot AI commented Jun 10, 2025

This PR extends PredefinedDynamicTraceEventParser to support dynamic events from EventPipeEventSource in addition to ETWTraceEventSource.

Problem

Previously, PredefinedDynamicTraceEventParser.OnUnhandledEvent only handled ETW events using RegisteredTraceEventParser.TryLookupWorker(), which doesn't work for EventPipe events. This meant that predefined dynamic event templates couldn't be used with EventPipe trace sources.

Solution

Modified the OnUnhandledEvent method to:

  1. Check source type: Use data.Source as EventPipeEventSource to detect EventPipe sources
  2. EventPipe path: For EventPipe sources, use EventPipeEventSource.TryGetTemplateFromMetadata() to retrieve event templates
  3. ETW path: For other sources, preserve existing behavior using RegisteredTraceEventParser.TryLookupWorker()
  4. Unified processing: Both paths converge to the same template registration logic
// Before (ETW only)
DynamicTraceEventData dynamicTraceEventData = RegisteredTraceEventParser.TryLookupWorker(data);

// After (ETW + EventPipe)
DynamicTraceEventData dynamicTraceEventData = null;
EventPipeEventSource eventPipeSource = data.Source as EventPipeEventSource;
if (eventPipeSource != null)
{
    eventPipeSource.TryGetTemplateFromMetadata(data, out dynamicTraceEventData);
}
else
{
    dynamicTraceEventData = RegisteredTraceEventParser.TryLookupWorker(data);
}

Benefits

  • ✅ Enables strongly-typed access to EventPipe dynamic events via predefined templates
  • ✅ Zero breaking changes to existing ETW functionality
  • ✅ Minimal code footprint (15 lines added, 2 modified)
  • ✅ Follows established patterns used in EventPipeTraceEventParser.TryLookup
  • ✅ Maintains backward compatibility

The implementation follows the same architectural pattern already established in the codebase and enables unified dynamic event handling across both ETW and EventPipe trace sources.

Fixes #2231.

Co-authored-by: brianrob <6210322+brianrob@users.noreply.github.com>
@Copilot Copilot AI changed the title [WIP] Extend PredefinedDynamicTraceEventParser to Support Dynamic Events from EventPipeEventSource Extend PredefinedDynamicTraceEventParser to Support Dynamic Events from EventPipeEventSource Jun 10, 2025
@Copilot Copilot AI requested a review from brianrob June 10, 2025 23:57
Copilot finished work on behalf of brianrob June 10, 2025 23:57
@brianrob
Copy link
Member

@copilot, this code looks correct. I'd now like for you to update UniversalEventsTraceEventParser to inherit from PredefinedDynamicTraceEventParser. Be sure to update each of the events as well. The event payloads are the same and don't need to be changed, but the events need to be converted to the pattern that PredefinedDynamicTraceEventParser follows.

…micTraceEventParser

Co-authored-by: brianrob <6210322+brianrob@users.noreply.github.com>
Copy link
Contributor Author

Copilot AI commented Jun 11, 2025

@copilot, this code looks correct. I'd now like for you to update UniversalEventsTraceEventParser to inherit from PredefinedDynamicTraceEventParser. Be sure to update each of the events as well....

I've updated UniversalEventsTraceEventParser to inherit from PredefinedDynamicTraceEventParser and converted the events to follow the PredefinedDynamicTraceEventParser pattern. The changes include:

  1. Changed inheritance from TraceEventParser to PredefinedDynamicTraceEventParser
  2. Created CpuSampleEvent and CswitchSampleEvent classes that inherit from PredefinedDynamicEvent
  3. Registered the event templates in the constructor using RegisterTemplate()
  4. Removed the custom EnumerateTemplates method to use the base class implementation
  5. Maintained backward compatibility by keeping the existing event signatures with SampleTraceData

The implementation provides both dynamic event support (for EventPipe sources) and backward compatibility for existing code. Commit: 87a570a

Copilot finished work on behalf of brianrob June 11, 2025 00:24
@brianrob
Copy link
Member

@copilot please update the cpu and cswitch events in UniversalEventsTraceEventParser to properly use the registered dynamic events.

Co-authored-by: brianrob <6210322+brianrob@users.noreply.github.com>
Copy link
Contributor Author

Copilot AI commented Jun 11, 2025

@copilot please update the cpu and cswitch events in UniversalEventsTraceEventParser to properly use the registered dynamic events.

I've updated the cpu and cswitch events in UniversalEventsTraceEventParser to properly use the registered dynamic events. The changes include:

  1. Changed event signatures from Action<SampleTraceData> to Action<CpuSampleEvent> and Action<CswitchSampleEvent>
  2. Updated event implementation to use AddCallbackForEvent<T>() and RemoveCallback<T>() instead of manual template registration
  3. Updated consuming code in PerfViewData.cs and NettraceUniversalConverter.cs to use the new event types

The events now properly leverage the PredefinedDynamicTraceEventParser infrastructure and work with the registered dynamic event templates. Commit: 48c23bd

Copilot finished work on behalf of brianrob June 11, 2025 00:39
@@ -97,8 +98,24 @@ protected internal override void EnumerateTemplates(Func<string, string, EventFi
/// </summary>
private bool OnUnhandledEvent(TraceEvent data)
{
// Parse event metadata.
DynamicTraceEventData dynamicTraceEventData = RegisteredTraceEventParser.TryLookupWorker(data);
// Parse event metadata based on the source type
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is a more structural problem here that I hadn't appreciated. PredefinedDynamicTraceEventParser has an implementation that handles unknown events, and so does ExternalTraceEventParser. I think we should make PredefinedDynamicTraceEventParser inherit from ExternalTraceEventParser and that way it can use the implementation for unknown events in ExternalTraceEventParser. This will also require changing the implementation of ExternalTraceEventParser - specifically the implementation of delegate passed to RegisterUnhandledEvent so that it can find both events in the ExternalTraceEventParserState and those in PredefinedDynamicTraceEventParser. I think that we should be able to merge their implementations into ExternalTraceEventParser instead of having a separate implementation in PredefinedDynamicTraceEventParser.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've refactored the architecture as requested. Changes made:

  1. Changed inheritance: PredefinedDynamicTraceEventParser now inherits from ExternalTraceEventParser instead of TraceEventParser
  2. Implemented TryLookup: Replaced the custom OnUnhandledEvent handler with the standard TryLookup method
  3. Eliminated duplication: Removed the separate unhandled event registration logic
  4. Unified implementation: Both ETW and EventPipe event metadata parsing now use the same ExternalTraceEventParser framework

The implementations are now merged - ExternalTraceEventParser handles the unhandled event registration, and PredefinedDynamicTraceEventParser.TryLookup provides the predefined dynamic event lookup. This eliminates the structural problem while maintaining all existing functionality.

Commit: 618992c

Copilot finished work on behalf of brianrob June 16, 2025 23:53
brianrob added 2 commits June 17, 2025 17:17
PredefinedDynamicTraceEventParser.  Ensure we don't spend lots of time
decoding metadata over and over.
using Microsoft.Diagnostics.Tracing.Parsers;
using Microsoft.Diagnostics.Tracing.Parsers.Universal.Events;

string inputFileName = args[0];
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@copilot, this file contains a standalone repro test case that can be built and run, passing in an argument with the path to repro.nettrace.

Current Behavior: If an instance of DynamicParser is used to parse an event, then the event is not properly dispatched to the UniversalEventsTraceEventParser. For this repro case, the CPU events are written to the console with the prefix "Dynamic Parser:".

Expected Behavior: I expect the events to be dispatched by both the DynamicTraceEventParser and the UniversalEventsTraceEventParser if they both register to receive the event. Thus for cpu events, I expect to see console output that is prefixed by Dynamic Parser: and then on a separate line CPU:. Which of these prefixes shows up first does not matter.

Given that this is not working, can you please build and run the test case, confirm that is not working, and see if you can develop a fix.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've confirmed the issue and identified the root cause. The problem occurs when both DynamicTraceEventParser and UniversalEventsTraceEventParser register handlers for the same event - only one parser receives the events due to template registration conflicts.

Key Findings:

  • UniversalEventsTraceEventParser works correctly when used alone
  • ✅ Both parsers work when handling different events
  • ❌ When both handle the same event, only the first-registered parser gets events

Current Status:

I've made partial fixes to the template registration mechanism, but the core issue with multiple parsers handling identical events still needs to be resolved. The template chaining logic exists but appears to have dispatch issues.

Working on a complete solution to ensure both parsers can receive the same events simultaneously. Commit: 37104ad

Copilot AI and others added 2 commits June 18, 2025 00:56
…lEventsTraceEventParser

Co-authored-by: brianrob <6210322+brianrob@users.noreply.github.com>
Co-authored-by: brianrob <6210322+brianrob@users.noreply.github.com>
@Copilot Copilot AI requested a review from brianrob June 18, 2025 01:04
Copilot finished work on behalf of brianrob June 18, 2025 01:04
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Extend PredefinedDynamicTraceEventParser to Support Dynamic Events from EventPipeEventSource
2 participants