diff --git a/src/TraceEvent/PredefinedDynamicEvent.cs b/src/TraceEvent/PredefinedDynamicEvent.cs index c663512c9..287e9d792 100644 --- a/src/TraceEvent/PredefinedDynamicEvent.cs +++ b/src/TraceEvent/PredefinedDynamicEvent.cs @@ -58,16 +58,20 @@ protected internal override Delegate Target } /// - /// Creates a clone of this template. + /// Copies this event, or clones an unpopulated template for registration. /// - /// A new instance of the template + /// An independent event copy or template. public override unsafe TraceEvent Clone() { var clone = (PredefinedDynamicEvent)base.Clone(); - // Reset source-specific fields to ensure the clone can be registered with a new source - clone.traceEventSource = null; - clone.Target = null; + // Reset source-specific fields only for templates so they can be registered with a new source. + // Populated event copies retain their source and callback, matching the base Clone behavior. + if (eventRecord == null) + { + clone.traceEventSource = null; + clone.Target = null; + } return clone; } diff --git a/src/TraceEvent/TraceEvent.Tests/Universal/UniversalTests.cs b/src/TraceEvent/TraceEvent.Tests/Universal/UniversalTests.cs index fd6ab55c3..c786c4e2c 100644 --- a/src/TraceEvent/TraceEvent.Tests/Universal/UniversalTests.cs +++ b/src/TraceEvent/TraceEvent.Tests/Universal/UniversalTests.cs @@ -1,4 +1,6 @@ -using Microsoft.Diagnostics.Tracing.Parsers.Universal.Events; +using Microsoft.Diagnostics.Tracing; +using Microsoft.Diagnostics.Tracing.Etlx; +using Microsoft.Diagnostics.Tracing.Parsers.Universal.Events; using System; using Xunit; using Xunit.Abstractions; @@ -12,6 +14,45 @@ public UniversalTests(ITestOutputHelper output) { } + [Theory] + [InlineData("cpu")] + [InlineData("cswitch")] + public unsafe void SampleClone_PreservesSourceAndCallback(string eventName) + { + using var source = (TraceLog)Activator.CreateInstance(typeof(TraceLog), true); + Action callback = _ => { }; + TraceEventNativeMethods.EVENT_RECORD record = default; + var original = new SampleTraceData(eventName) + { + traceEventSource = source, + eventRecord = &record, + Target = callback, + }; + + var clone = original.Clone(); + + Assert.Same(source, clone.Source); + Assert.Same(callback, clone.Target); + } + + [Fact] + public void SampleTemplateClone_ClearsSourceAndCallback() + { + using var source = (TraceLog)Activator.CreateInstance(typeof(TraceLog), true); + var template = new SampleTraceData("cpu") + { + traceEventSource = source, + Target = new Action(_ => { }), + }; + + var clone = template.Clone(); + + Assert.Null(clone.Source); + Assert.Null(clone.Target); + Assert.Same(source, template.Source); + Assert.NotNull(template.Target); + } + [Fact] public void ProcessMappingMetadataParser_Empty() {