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()
{