diff --git a/eng/Versions.props b/eng/Versions.props index f3d3db85315ec1..837341dde90148 100644 --- a/eng/Versions.props +++ b/eng/Versions.props @@ -110,6 +110,7 @@ 2.0.0 17.10.0-beta1.24272.1 3.1.16 + 0.2.621003 2.1.0 2.0.3 1.0.4-preview6.19326.1 diff --git a/src/libraries/System.Diagnostics.Tracing/tests/BasicEventSourceTest/Harness/EtwListener.cs b/src/libraries/System.Diagnostics.Tracing/tests/BasicEventSourceTest/Harness/EtwListener.cs index 13750cc99b7826..179be31081f980 100644 --- a/src/libraries/System.Diagnostics.Tracing/tests/BasicEventSourceTest/Harness/EtwListener.cs +++ b/src/libraries/System.Diagnostics.Tracing/tests/BasicEventSourceTest/Harness/EtwListener.cs @@ -32,20 +32,25 @@ internal static void EnsureStopped() public EtwListener(string dataFileName = "EventSourceTestData.etl", string sessionName = "EventSourceTestSession") { _dataFileName = dataFileName; + _sessionName = sessionName; + _pendingCommands = new List<(string eventSourceName, EventCommand command, FilteringOptions options)>(); // Today you have to be Admin to turn on ETW events (anyone can write ETW events). if (TraceEventSession.IsElevated() != true) { throw new SkipTestException("Need to be elevated to run. "); } + } - if (dataFileName == null) + public override void Start() + { + if (_dataFileName == null) { - Debug.WriteLine("Creating a real time session " + sessionName); + Debug.WriteLine("Creating a real time session " + _sessionName); Task.Factory.StartNew(delegate () { - var session = new TraceEventSession(sessionName, dataFileName); + var session = new TraceEventSession(_sessionName, _dataFileName); session.Source.AllEvents += OnEventHelper; Debug.WriteLine("Listening for real time events"); _session = session; // Indicate that we are alive. @@ -58,13 +63,31 @@ public EtwListener(string dataFileName = "EventSourceTestData.etl", string sessi else { // Normalize to a full path name. - dataFileName = Path.GetFullPath(dataFileName); - Debug.WriteLine("Creating ETW data file " + Path.GetFullPath(dataFileName)); - _session = new TraceEventSession(sessionName, dataFileName); + _dataFileName = Path.GetFullPath(_dataFileName); + Debug.WriteLine("Creating ETW data file " + Path.GetFullPath(_dataFileName)); + _session = new TraceEventSession(_sessionName, _dataFileName); + } + foreach(var cmd in _pendingCommands) + { + ApplyEventSourceCommand(cmd.eventSourceName, cmd.command, cmd.options); } } + public override bool IsDynamicConfigChangeSupported => true; + public override void EventSourceCommand(string eventSourceName, EventCommand command, FilteringOptions options = null) + { + if (_session == null) + { + _pendingCommands.Add((eventSourceName, command, options)); + } + else + { + ApplyEventSourceCommand(eventSourceName, command, options); + } + } + + private void ApplyEventSourceCommand(string eventSourceName, EventCommand command, FilteringOptions options = null) { if (command == EventCommand.Enable) { @@ -111,6 +134,8 @@ public override void Dispose() } } + public override string ToString() => "EtwListener"; + #region private private void OnEventHelper(TraceEvent data) { @@ -136,7 +161,8 @@ private void OnEventHelper(TraceEvent data) /// internal class EtwEvent : Event { - public override bool IsEtw { get { return true; } } + public override bool IsEnumValueStronglyTyped(bool selfDescribing, bool isWriteEvent) => !selfDescribing; + public override bool IsSizeAndPointerCoallescedIntoSingleArg => true; public override string ProviderName { get { return _data.ProviderName; } } public override string EventName { get { return _data.EventName; } } public override object PayloadValue(int propertyIndex, string propertyName) @@ -162,7 +188,9 @@ public override string PayloadString(int propertyIndex, string propertyName) private bool _disposed; private string _dataFileName; + private string _sessionName; private volatile TraceEventSession _session; + private List<(string eventSourceName, EventCommand command, FilteringOptions options)> _pendingCommands; #endregion } diff --git a/src/libraries/System.Diagnostics.Tracing/tests/BasicEventSourceTest/Harness/EventPipeListener.cs b/src/libraries/System.Diagnostics.Tracing/tests/BasicEventSourceTest/Harness/EventPipeListener.cs new file mode 100644 index 00000000000000..b1156bf116297d --- /dev/null +++ b/src/libraries/System.Diagnostics.Tracing/tests/BasicEventSourceTest/Harness/EventPipeListener.cs @@ -0,0 +1,179 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Diagnostics.Tracing; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.Diagnostics.NETCore.Client; +using Microsoft.Diagnostics.Tracing; +using Xunit; + +namespace BasicEventSourceTests +{ + /// + /// Implementation of Listener for EventPipe (in-process collection using DiagnosticsClient + EventPipeEventSource). + /// + internal sealed class EventPipeListener : Listener + { + private readonly List<(string eventSourceName, EventCommand command, FilteringOptions options)> _pendingCommands = new(); + private readonly Dictionary _enabled = new(StringComparer.Ordinal); + private EventPipeSession _session; + private Task _processingTask; + private bool _disposed; + + public override bool IsDynamicConfigChangeSupported => false; + + /// + /// EventPipe NetTrace V5 format can't emit the metadata for a Boolean8 HasValue field that self-describing events use. + /// + public override bool IsSelfDescribingNullableSupported => false; + + public override bool IsEventPipe => true; + + public EventPipeListener() { } + + public override void EventSourceCommand(string eventSourceName, EventCommand command, FilteringOptions options = null) + { + if (eventSourceName is null) + { + throw new ArgumentNullException(nameof(eventSourceName)); + } + + if (_session != null) + { + throw new InvalidOperationException("EventPipeEventListener does not support dynamic configuration changes after Start()."); + } + _pendingCommands.Add((eventSourceName, command, options)); + } + + public override void Start() + { + if (_session != null) + { + return; // already started + } + + // Build provider enable list from pending commands + foreach (var (eventSourceName, command, options) in _pendingCommands) + { + if (command == EventCommand.Enable) + { + var effective = options ?? new FilteringOptions(); + _enabled[eventSourceName] = effective; + } + else if (command == EventCommand.Disable) + { + _enabled.Remove(eventSourceName); + } + else + { + throw new NotImplementedException(); + } + } + + var providers = new List(); + foreach (var kvp in _enabled) + { + var opt = kvp.Value; + providers.Add(new EventPipeProvider(kvp.Key, (EventLevel)opt.Level, (long)opt.Keywords, opt.Args)); + } + + var client = new DiagnosticsClient(Environment.ProcessId); + _session = client.StartEventPipeSession(providers, false); + + _processingTask = Task.Factory.StartNew(() => ProcessEvents(_session), TaskCreationOptions.LongRunning); + } + + private void ProcessEvents(EventPipeSession session) + { + using var source = new EventPipeEventSource(session.EventStream); + source.Dynamic.All += traceEvent => + { + // EventPipe adds extra events we didn't ask for, ignore them. + if (traceEvent.ProviderName == "Microsoft-DotNETCore-EventPipe") + { + return; + } + + OnEvent?.Invoke(new EventPipeEvent(traceEvent)); + }; + source.Process(); + } + + public override void Dispose() + { + if (_disposed) + { + return; + } + + try + { + _disposed = true; + _session?.Stop(); + + if (_processingTask != null && !_processingTask.Wait(TimeSpan.FromSeconds(5))) + { + // If the session is still streaming data then session.Dispose() below will disconnect the stream + // and likely cause the thread running source.Process() to throw. + Assert.Fail("EventPipeEventListener processing task failed to complete in 5 seconds."); + } + } + finally + { + _session?.Dispose(); + } + } + + public override string ToString() => "EventPipeListener"; + + /// + /// Wrapper mapping TraceEvent (EventPipe) to harness Event abstraction. + /// + private sealed class EventPipeEvent : Event + { + private readonly TraceEvent _data; + private readonly IList _payloadNames; + private readonly IList _payloadValues; + + public EventPipeEvent(TraceEvent data) + { + _data = data; + // EventPipe has a discrepancy with ETW for self-describing events - it exposes a single top-level object whereas ETW considers each of the fields within + // that object as top-level named fields. To workaround that we unwrap any top-level object at payload index 0. + if(data.PayloadNames.Length > 0 && data.PayloadValue(0) is IDictionary d) + { + _payloadNames = d.Select(kv => kv.Key).ToList(); + _payloadValues = d.Select(kv => kv.Value).ToList(); + } + else + { + _payloadNames = data.PayloadNames; + _payloadValues = new List(); + for(int i = 0; i < _payloadNames.Count; i++) + { + _payloadValues.Add(data.PayloadValue(i)); + } + } + } + + public override string ProviderName => _data.ProviderName; + public override string EventName => _data.EventName; + public override int PayloadCount => _payloadNames.Count; + public override IList PayloadNames => _payloadNames; + + public override object PayloadValue(int propertyIndex, string propertyName) + { + if (propertyName != null) + { + Assert.Equal(propertyName, _payloadNames[propertyIndex]); + } + return _payloadValues[propertyIndex]; + } + } + } +} diff --git a/src/libraries/System.Diagnostics.Tracing/tests/BasicEventSourceTest/Harness/EventTestHarness.cs b/src/libraries/System.Diagnostics.Tracing/tests/BasicEventSourceTest/Harness/EventTestHarness.cs index 278a8bbedf4279..669016face5577 100644 --- a/src/libraries/System.Diagnostics.Tracing/tests/BasicEventSourceTest/Harness/EventTestHarness.cs +++ b/src/libraries/System.Diagnostics.Tracing/tests/BasicEventSourceTest/Harness/EventTestHarness.cs @@ -112,9 +112,14 @@ public static void RunTests(List tests, Listener listener, EventSource using (TestHarnessEventSource testHarnessEventSource = new TestHarnessEventSource()) { // Turn on the test EventSource. - listener.EventSourceSynchronousEnable(source, options); + listener.EventSourceCommand(source.Name, EventCommand.Enable, options); // And the harnesses's EventSource. - listener.EventSourceSynchronousEnable(testHarnessEventSource); + listener.EventSourceCommand(testHarnessEventSource.Name, EventCommand.Enable); + + // Start the session and wait for the sources to be enabled. + listener.Start(); + listener.WaitForEventSourceStateChange(source, true); + listener.WaitForEventSourceStateChange(testHarnessEventSource, true); // Generate events for all the tests, surrounded by events that tell us we are starting a test. int testNumber = 0; @@ -126,12 +131,16 @@ public static void RunTests(List tests, Listener listener, EventSource } testHarnessEventSource.StartTest("", testNumber); // Empty test marks the end of testing. - // Disable the listeners. - listener.EventSourceCommand(source.Name, EventCommand.Disable); - listener.EventSourceCommand(testHarnessEventSource.Name, EventCommand.Disable); + + if (listener.IsDynamicConfigChangeSupported) + { + // Disable the listeners. + listener.EventSourceSynchronousDisable(source); + listener.EventSourceSynchronousDisable(testHarnessEventSource); - // Send something that should be ignored. - testHarnessEventSource.IgnoreEvent(); + // Send something that should be ignored. + testHarnessEventSource.IgnoreEvent(); + } } } catch (Exception e) @@ -164,8 +173,8 @@ public static void RunTests(List tests, Listener listener, EventSource listener.Dispose(); // Indicate we are done listening. For the ETW file based cases, we do all the processing here - // expectedTetst number are the number of tests we successfully ran. - Assert.Equal(expectedTestNumber, tests.Count); + int actualTestsRun = expectedTestNumber; + Assert.Equal(tests.Count, actualTestsRun); } public class EventTestHarnessException : Exception diff --git a/src/libraries/System.Diagnostics.Tracing/tests/BasicEventSourceTest/Harness/Listeners.cs b/src/libraries/System.Diagnostics.Tracing/tests/BasicEventSourceTest/Harness/Listeners.cs index 08c02712f38129..d0573c4ee82fa7 100644 --- a/src/libraries/System.Diagnostics.Tracing/tests/BasicEventSourceTest/Harness/Listeners.cs +++ b/src/libraries/System.Diagnostics.Tracing/tests/BasicEventSourceTest/Harness/Listeners.cs @@ -11,27 +11,69 @@ namespace BasicEventSourceTests { /// - /// A listener can represent an out of process ETW listener (real time or not) or an EventListener + /// A listener can represent an out of process ETW listener (real time or not), an EventPipe listener, or an EventListener /// public abstract class Listener : IDisposable { public Action OnEvent; // Called when you get events. public abstract void Dispose(); + /// - /// Send a command to an eventSource. Be careful this is async. You may wish to do a WaitForEnable + /// Send a command to an eventSource. If this is called before Start(), the command will be queued. If called after Start() + /// it will throw if !IsDynamicConfigChangeSupported /// public abstract void EventSourceCommand(string eventSourceName, EventCommand command, FilteringOptions options = null); + /// + /// Start listening for events + /// + public abstract void Start(); + + /// + /// True if this listener supports dynamic config changes (i.e. EventSourceCommand) after Start() has been called. + /// ETW and EventListener support this, EventPipe does not. + /// + public abstract bool IsDynamicConfigChangeSupported { get; } + + /// + /// Does this listener support nullable types in event payloads for self-describing EventSources? + /// Ideally all of them would but EventPipe NetTrace V5 format can't emit the metadata for a Boolean8 HasValue field that self-describing events use. + /// + public virtual bool IsSelfDescribingNullableSupported { get { return true; } } + + /// + /// The TraceLogging serializer doesn't support null arguments in self-describing events. + /// (Sigh, ideally all of them would behave the same way but EventListener does support this and for backwards compatibility we aren't going to change it) + /// + public virtual bool IsSelfDescribingNullArgSupported { get { return false; } } + + public virtual bool IsEventPipe { get { return false; } } + public void EventSourceSynchronousEnable(EventSource eventSource, FilteringOptions options = null) { + if (!IsDynamicConfigChangeSupported) + { + throw new InvalidOperationException("This listener does not support dynamic config changes"); + } EventSourceCommand(eventSource.Name, EventCommand.Enable, options); - WaitForEnable(eventSource); + WaitForEventSourceStateChange(eventSource, true); } - public void WaitForEnable(EventSource logger) + + public void EventSourceSynchronousDisable(EventSource eventSource) { - if (!SpinWait.SpinUntil(() => logger.IsEnabled(), TimeSpan.FromSeconds(10))) + if (!IsDynamicConfigChangeSupported) { - throw new InvalidOperationException("EventSource not enabled after 5 seconds"); + throw new InvalidOperationException("This listener does not support dynamic config changes"); + } + EventSourceCommand(eventSource.Name, EventCommand.Disable); + WaitForEventSourceStateChange(eventSource, false); + } + + public void WaitForEventSourceStateChange(EventSource logger, bool targetState) + { + if (!SpinWait.SpinUntil(() => logger.IsEnabled() == targetState, TimeSpan.FromSeconds(10))) + { + throw new InvalidOperationException("EventSource not enabled after 10 seconds"); } } @@ -65,8 +107,12 @@ public class FilteringOptions /// public abstract class Event { - public virtual bool IsEtw { get { return false; } } public virtual bool IsEventListener { get { return false; } } + // Note: Observationally I am seeing that EventListener events treat enum values differently in the WriteEvent vs. Write case but + // I'm not sure whether that is the determining factor or its just correlated with other details of how the tests are emitting the events. + public virtual bool IsEnumValueStronglyTyped(bool selfDescribing, bool writeEvent) => false; + + public virtual bool IsSizeAndPointerCoallescedIntoSingleArg => false; public abstract string ProviderName { get; } public abstract string EventName { get; } public abstract object PayloadValue(int propertyIndex, string propertyName); @@ -80,13 +126,13 @@ public virtual string PayloadString(int propertyIndex, string propertyName) StringBuilder sb = new StringBuilder(); sb.Append("{"); bool first = true; - foreach (var key in asDict.Keys) + foreach (var keyValue in asDict) { if (!first) sb.Append(","); first = false; - var value = asDict[key]; - sb.Append(key).Append(":").Append(value != null ? value.ToString() : "NULL"); + var value = keyValue.Value; + sb.Append(keyValue.Key).Append(":").Append(value != null ? value.ToString() : "NULL"); } sb.Append("}"); return sb.ToString(); @@ -131,6 +177,8 @@ public class EventListenerListener : Listener private EventListener _listener; private Action _onEventSourceCreated; + public override bool IsSelfDescribingNullArgSupported => true; + public event EventHandler EventSourceCreated { add @@ -161,7 +209,13 @@ public event EventHandler EventWritten public EventListenerListener(bool useEventsToListen = false) { - if (useEventsToListen) + _useEventsToListen = useEventsToListen; + _pendingCommands = new List<(string eventSourceName, EventCommand command, FilteringOptions options)>(); + } + + public override void Start() + { + if (_useEventsToListen) { _listener = new HelperEventListener(null); _listener.EventSourceCreated += (sender, eventSourceCreatedEventArgs) @@ -172,8 +226,14 @@ public EventListenerListener(bool useEventsToListen = false) { _listener = new HelperEventListener(this); } + foreach (var cmd in _pendingCommands) + { + ApplyEventSourceCommand(cmd.eventSourceName, cmd.command, cmd.options); + } } + public override bool IsDynamicConfigChangeSupported => true; + public override void Dispose() { if (_disposed) @@ -197,6 +257,18 @@ private void DoCommand(EventSource source, EventCommand command, FilteringOption } public override void EventSourceCommand(string eventSourceName, EventCommand command, FilteringOptions options = null) + { + if (_listener == null) + { + _pendingCommands.Add((eventSourceName, command, options)); + } + else + { + ApplyEventSourceCommand(eventSourceName, command, options); + } + } + + private void ApplyEventSourceCommand(string eventSourceName, EventCommand command, FilteringOptions options = null) { EventTestHarness.LogWriteLine("Sending command {0} to EventSource {1} Options {2}", eventSourceName, command, options); @@ -222,6 +294,8 @@ public override void EventSourceCommand(string eventSourceName, EventCommand com }; } + public override string ToString() => $"EventListener(UseEventsToListen={_useEventsToListen})"; + private void mListenerEventWritten(object sender, EventWrittenEventArgs eventData) { OnEvent?.Invoke(new EventListenerEvent(eventData)); @@ -261,6 +335,7 @@ internal class EventListenerEvent : Event internal EventListenerEvent(EventWrittenEventArgs data) => Data = data; public override bool IsEventListener { get { return true; } } + public override bool IsEnumValueStronglyTyped(bool selfDescribing, bool isWriteEvent) => !isWriteEvent; public override string ProviderName { get { return Data.EventSource.Name; } } @@ -282,6 +357,8 @@ public override object PayloadValue(int propertyIndex, string propertyName) } } + private List<(string eventSourceName, EventCommand command, FilteringOptions options)> _pendingCommands; + private bool _useEventsToListen = false; private bool _disposed; } } diff --git a/src/libraries/System.Diagnostics.Tracing/tests/BasicEventSourceTest/TestsUserErrors.cs b/src/libraries/System.Diagnostics.Tracing/tests/BasicEventSourceTest/TestsUserErrors.cs index 273b6b3e3670ba..d6cf287f4bf53c 100644 --- a/src/libraries/System.Diagnostics.Tracing/tests/BasicEventSourceTest/TestsUserErrors.cs +++ b/src/libraries/System.Diagnostics.Tracing/tests/BasicEventSourceTest/TestsUserErrors.cs @@ -35,6 +35,7 @@ private void Test_BadTypes_Manifest(EventSource source) Debug.WriteLine("Adding delegate to onevent"); listener.OnEvent = delegate (Event data) { events.Add(data); }; + listener.Start(); listener.EventSourceCommand(source.Name, EventCommand.Enable); listener.Dispose(); @@ -95,9 +96,13 @@ private void Test_Bad_EventSource_Startup(bool onStartup, Listener listener, Eve var eventSourceName = typeof(BadEventSource_MismatchedIds).Name; Debug.WriteLine("***** Test_BadEventSource_Startup(OnStartUp: " + onStartup + " Listener: " + listener + " Settings: " + settings + ")"); + listener.EventSourceCommand(eventSourceName, EventCommand.Enable); + // Activate the source before the source exists (if told to). if (onStartup) - listener.EventSourceCommand(eventSourceName, EventCommand.Enable); + { + listener.Start(); + } var events = new List(); listener.OnEvent = delegate (Event data) { events.Add(data); }; @@ -107,7 +112,9 @@ private void Test_Bad_EventSource_Startup(bool onStartup, Listener listener, Eve Assert.Equal(eventSourceName, source.Name); // activate the source after the source exists (if told to). if (!onStartup) - listener.EventSourceCommand(eventSourceName, EventCommand.Enable); + { + listener.Start(); + } source.Event1(1); // Try to send something. } listener.Dispose(); @@ -160,7 +167,7 @@ public void Test_Bad_WriteRelatedID_ParameterName() { var events = new List(); listener.OnEvent = delegate (Event data) { events.Add(data); }; - + listener.Start(); listener.EventSourceCommand(bes.Name, EventCommand.Enable); bes.RelatedActivity(newGuid2, "Hello", 42, "AA", "BB"); diff --git a/src/libraries/System.Diagnostics.Tracing/tests/BasicEventSourceTest/TestsWrite.Etw.cs b/src/libraries/System.Diagnostics.Tracing/tests/BasicEventSourceTest/TestsWrite.Etw.cs index 2f26c957f5b1fe..663e012ad2e36e 100644 --- a/src/libraries/System.Diagnostics.Tracing/tests/BasicEventSourceTest/TestsWrite.Etw.cs +++ b/src/libraries/System.Diagnostics.Tracing/tests/BasicEventSourceTest/TestsWrite.Etw.cs @@ -19,7 +19,6 @@ public partial class TestsWrite /// Tests the ETW code path /// [ConditionalFact(nameof(IsProcessElevatedAndNotWindowsNanoServer))] - [ActiveIssue("https://github.com/dotnet/runtime/issues/88305")] public void Test_Write_T_ETW() { using (var listener = new EtwListener()) diff --git a/src/libraries/System.Diagnostics.Tracing/tests/BasicEventSourceTest/TestsWrite.cs b/src/libraries/System.Diagnostics.Tracing/tests/BasicEventSourceTest/TestsWrite.cs index 6371f7290f92b4..b5d71dacd83346 100644 --- a/src/libraries/System.Diagnostics.Tracing/tests/BasicEventSourceTest/TestsWrite.cs +++ b/src/libraries/System.Diagnostics.Tracing/tests/BasicEventSourceTest/TestsWrite.cs @@ -20,43 +20,39 @@ internal enum ColorUInt64 : ulong { Red, Blue, Green }; public partial class TestsWrite { - [EventData] private struct PartB_UserInfo { public string UserName { get; set; } } - /// - /// Tests the EventSource.Write[T] method (can only use the self-describing mechanism). - /// Tests the EventListener code path - /// - [Fact] - [ActiveIssue("https://github.com/dotnet/runtime/issues/21564", TargetFrameworkMonikers.NetFramework)] - public void Test_Write_T_EventListener() + public static TheoryData GetListeners() { - using (var listener = new EventListenerListener()) + TheoryData data = new TheoryData(); + + if (PlatformDetection.IsNetCore && PlatformDetection.IsNotAndroid && PlatformDetection.IsNotBrowser && + (PlatformDetection.IsNotMonoRuntime || PlatformDetection.IsMacCatalyst)) { - Test_Write_T(listener); + data.Add(new EventPipeListener()); } + data.Add(new EventListenerListener()); + data.Add(new EventListenerListener(true)); + return data; } - /// - /// Tests the EventSource.Write[T] method (can only use the self-describing mechanism). - /// Tests the EventListener code path using events instead of virtual callbacks. - /// - [Fact] + [Theory] + [MemberData(nameof(GetListeners))] [ActiveIssue("https://github.com/dotnet/runtime/issues/21564", TargetFrameworkMonikers.NetFramework)] - public void Test_Write_T_EventListener_UseEvents() + public void Test_Write_T(Listener listener) { - Test_Write_T(new EventListenerListener(true)); + Test_Write_T_Helper(listener); } /// /// Te /// /// - private void Test_Write_T(Listener listener) + private void Test_Write_T_Helper(Listener listener) { TestUtilities.CheckNoEventSourcesRunning("Start"); @@ -109,7 +105,9 @@ private void Test_Write_T(Listener listener) })); /*************************************************************************/ byte[] byteArray = { 0, 1, 2, 3 }; - tests.Add(new SubTest("Write/Basic/byte[]", + if (!listener.IsEventPipe) // EventPipe does not format metadata correctly for scalar arrays + { + tests.Add(new SubTest("Write/Basic/byte[]", delegate () { logger.Write("Bytes", new { bytes = byteArray }); @@ -120,68 +118,73 @@ private void Test_Write_T(Listener listener) Assert.Equal("Bytes", evt.EventName); var eventArray = evt.PayloadValue(0, "bytes"); - Array.Equals(eventArray, byteArray); + Assert.Equal(eventArray, byteArray); })); - /*************************************************************************/ - int? nullableInt = 12; - tests.Add(new SubTest("Write/Basic/int?/12", - delegate () - { - logger.Write("Int12", new { nInteger = nullableInt }); - }, - delegate (Event evt) - { - Assert.Equal(logger.Name, evt.ProviderName); - Assert.Equal("Int12", evt.EventName); - - var payload = evt.PayloadValue(0, "nInteger"); - Assert.Equal(nullableInt, TestUtilities.UnwrapNullable(payload)); - })); - /*************************************************************************/ - int? nullableInt2 = null; - tests.Add(new SubTest("Write/Basic/int?/null", - delegate () - { - logger.Write("IntNull", new { nInteger = nullableInt2 }); - }, - delegate (Event evt) - { - Assert.Equal(logger.Name, evt.ProviderName); - Assert.Equal("IntNull", evt.EventName); - - var payload = evt.PayloadValue(0, "nInteger"); - Assert.Equal(nullableInt2, TestUtilities.UnwrapNullable(payload)); - })); - ///*************************************************************************/ - DateTime? nullableDate = DateTime.Now; - tests.Add(new SubTest("Write/Basic/DateTime?/Now", - delegate () - { - logger.Write("DateTimeNow", new { nowTime = nullableDate }); - }, - delegate (Event evt) - { - Assert.Equal(logger.Name, evt.ProviderName); - Assert.Equal("DateTimeNow", evt.EventName); + } - var payload = evt.PayloadValue(0, "nowTime"); - Assert.Equal(nullableDate, TestUtilities.UnwrapNullable(payload)); - })); - /*************************************************************************/ - DateTime? nullableDate2 = null; - tests.Add(new SubTest("Write/Basic/DateTime?/Null", - delegate () - { - logger.Write("DateTimeNull", new { nowTime = nullableDate2 }); - }, - delegate (Event evt) - { - Assert.Equal(logger.Name, evt.ProviderName); - Assert.Equal("DateTimeNull", evt.EventName); + if (listener.IsSelfDescribingNullableSupported) + { + /*************************************************************************/ + int? nullableInt = 12; + tests.Add(new SubTest("Write/Basic/int?/12", + delegate () + { + logger.Write("Int12", new { nInteger = nullableInt }); + }, + delegate (Event evt) + { + Assert.Equal(logger.Name, evt.ProviderName); + Assert.Equal("Int12", evt.EventName); + + var payload = evt.PayloadValue(0, "nInteger"); + Assert.Equal(nullableInt, TestUtilities.UnwrapNullable(payload)); + })); + /*************************************************************************/ + int? nullableInt2 = null; + tests.Add(new SubTest("Write/Basic/int?/null", + delegate () + { + logger.Write("IntNull", new { nInteger = nullableInt2 }); + }, + delegate (Event evt) + { + Assert.Equal(logger.Name, evt.ProviderName); + Assert.Equal("IntNull", evt.EventName); + + var payload = evt.PayloadValue(0, "nInteger"); + Assert.Equal(nullableInt2, TestUtilities.UnwrapNullable(payload)); + })); + ///*************************************************************************/ + DateTime? nullableDate = DateTime.Now; + tests.Add(new SubTest("Write/Basic/DateTime?/Now", + delegate () + { + logger.Write("DateTimeNow", new { nowTime = nullableDate }); + }, + delegate (Event evt) + { + Assert.Equal(logger.Name, evt.ProviderName); + Assert.Equal("DateTimeNow", evt.EventName); + + var payload = evt.PayloadValue(0, "nowTime"); + Assert.Equal(nullableDate, TestUtilities.UnwrapNullable(payload)); + })); + /*************************************************************************/ + DateTime? nullableDate2 = null; + tests.Add(new SubTest("Write/Basic/DateTime?/Null", + delegate () + { + logger.Write("DateTimeNull", new { nowTime = nullableDate2 }); + }, + delegate (Event evt) + { + Assert.Equal(logger.Name, evt.ProviderName); + Assert.Equal("DateTimeNull", evt.EventName); - var payload = evt.PayloadValue(0, "nowTime"); - Assert.Equal(nullableDate2, TestUtilities.UnwrapNullable(payload)); - })); + var payload = evt.PayloadValue(0, "nowTime"); + Assert.Equal(nullableDate2, TestUtilities.UnwrapNullable(payload)); + })); + } /*************************************************************************/ tests.Add(new SubTest("Write/Basic/PartBOnly", delegate () @@ -241,21 +244,25 @@ private void Test_Write_T(Listener listener) /*************************************************************************/ - GenerateArrayTest(ref tests, logger, new bool[] { false, true, false }); - GenerateArrayTest(ref tests, logger, new byte[] { 1, 10, 100 }); - GenerateArrayTest(ref tests, logger, new sbyte[] { 1, 10, 100 }); - GenerateArrayTest(ref tests, logger, new short[] { 1, 10, 100 }); - GenerateArrayTest(ref tests, logger, new ushort[] { 1, 10, 100 }); - GenerateArrayTest(ref tests, logger, new int[] { 1, 10, 100 }); - GenerateArrayTest(ref tests, logger, new uint[] { 1, 10, 100 }); - GenerateArrayTest(ref tests, logger, new long[] { 1, 10, 100 }); - GenerateArrayTest(ref tests, logger, new ulong[] { 1, 10, 100 }); - GenerateArrayTest(ref tests, logger, new char[] { 'a', 'c', 'b' }); - GenerateArrayTest(ref tests, logger, new double[] { 1, 10, 100 }); - GenerateArrayTest(ref tests, logger, new float[] { 1, 10, 100 }); - GenerateArrayTest(ref tests, logger, new IntPtr[] { (IntPtr)1, (IntPtr)10, (IntPtr)100 }); - GenerateArrayTest(ref tests, logger, new UIntPtr[] { (UIntPtr)1, (UIntPtr)10, (UIntPtr)100 }); - GenerateArrayTest(ref tests, logger, new Guid[] { Guid.Empty, new Guid("121a11ee-3bcb-49cc-b425-f4906fb14f72") }); + // EventPipe doesn't format metadata correctly for scalar arrays + if (!listener.IsEventPipe) + { + GenerateArrayTest(ref tests, logger, new bool[] { false, true, false }); + GenerateArrayTest(ref tests, logger, new byte[] { 1, 10, 100 }); + GenerateArrayTest(ref tests, logger, new sbyte[] { 1, 10, 100 }); + GenerateArrayTest(ref tests, logger, new short[] { 1, 10, 100 }); + GenerateArrayTest(ref tests, logger, new ushort[] { 1, 10, 100 }); + GenerateArrayTest(ref tests, logger, new int[] { 1, 10, 100 }); + GenerateArrayTest(ref tests, logger, new uint[] { 1, 10, 100 }); + GenerateArrayTest(ref tests, logger, new long[] { 1, 10, 100 }); + GenerateArrayTest(ref tests, logger, new ulong[] { 1, 10, 100 }); + GenerateArrayTest(ref tests, logger, new char[] { 'a', 'c', 'b' }); + GenerateArrayTest(ref tests, logger, new double[] { 1, 10, 100 }); + GenerateArrayTest(ref tests, logger, new float[] { 1, 10, 100 }); + GenerateArrayTest(ref tests, logger, new IntPtr[] { (IntPtr)1, (IntPtr)10, (IntPtr)100 }); + GenerateArrayTest(ref tests, logger, new UIntPtr[] { (UIntPtr)1, (UIntPtr)10, (UIntPtr)100 }); + GenerateArrayTest(ref tests, logger, new Guid[] { Guid.Empty, new Guid("121a11ee-3bcb-49cc-b425-f4906fb14f72") }); + } /*************************************************************************/ /*********************** DICTIONARY TESTING ******************************/ @@ -264,91 +271,97 @@ private void Test_Write_T(Listener listener) var dict = new Dictionary() { { "elem1", "10" }, { "elem2", "20" } }; var dictInt = new Dictionary() { { "elem1", 10 }, { "elem2", 20 } }; - /*************************************************************************/ - tests.Add(new SubTest("Write/Dict/EventWithStringDict_C", - delegate () - { - // log a dictionary - logger.Write("EventWithStringDict_C", new + // EventPipe doesn't serialize metadata for dictionary correctly + if (!listener.IsEventPipe) + { + /*************************************************************************/ + tests.Add(new SubTest("Write/Dict/EventWithStringDict_C", + delegate () { - myDict = dict, - s = "end" - }); - }, - delegate (Event evt) - { - Assert.Equal(logger.Name, evt.ProviderName); - Assert.Equal("EventWithStringDict_C", evt.EventName); - - var keyValues = evt.PayloadValue(0, "myDict"); - IDictionary vDict = GetDictionaryFromKeyValueArray(keyValues); - Assert.Equal("10", vDict["elem1"]); - Assert.Equal("20", vDict["elem2"]); - Assert.Equal("end", evt.PayloadValue(1, "s")); - })); - /*************************************************************************/ - tests.Add(new SubTest("Write/Dict/EventWithStringDict_BC", - delegate () - { - // log a PartB and a dictionary as a PartC - logger.Write("EventWithStringDict_BC", new + // log a dictionary + logger.Write("EventWithStringDict_C", new + { + myDict = dict, + s = "end" + }); + }, + delegate (Event evt) { - PartB_UserInfo = new { UserName = "Me", LogTime = "Now" }, - PartC_Dict = dict, - s = "end" - }); - }, - delegate (Event evt) - { - Assert.Equal(logger.Name, evt.ProviderName); - Assert.Equal("EventWithStringDict_BC", evt.EventName); - - var structValue = evt.PayloadValue(0, "PartB_UserInfo"); - var structValueAsDictionary = structValue as IDictionary; - Assert.NotNull(structValueAsDictionary); - Assert.Equal("Me", structValueAsDictionary["UserName"]); - Assert.Equal("Now", structValueAsDictionary["LogTime"]); - - var keyValues = evt.PayloadValue(1, "PartC_Dict"); - var vDict = GetDictionaryFromKeyValueArray(keyValues); - Assert.NotNull(dict); - Assert.Equal("10", vDict["elem1"]); // string values. - Assert.Equal("20", vDict["elem2"]); - - Assert.Equal("end", evt.PayloadValue(2, "s")); - })); - /*************************************************************************/ - tests.Add(new SubTest("Write/Dict/EventWithIntDict_BC", - delegate () - { - // log a Dict as a PartC - logger.Write("EventWithIntDict_BC", new + Assert.Equal(logger.Name, evt.ProviderName); + Assert.Equal("EventWithStringDict_C", evt.EventName); + + var keyValues = evt.PayloadValue(0, "myDict"); + IDictionary vDict = GetDictionaryFromKeyValueArray(keyValues); + Assert.Equal("10", vDict["elem1"]); + Assert.Equal("20", vDict["elem2"]); + Assert.Equal("end", evt.PayloadValue(1, "s")); + })); + /*************************************************************************/ + tests.Add(new SubTest("Write/Dict/EventWithStringDict_BC", + delegate () { - PartB_UserInfo = new { UserName = "Me", LogTime = "Now" }, - PartC_Dict = dictInt, - s = "end" - }); - - }, - delegate (Event evt) - { - Assert.Equal(logger.Name, evt.ProviderName); - Assert.Equal("EventWithIntDict_BC", evt.EventName); - - var structValue = evt.PayloadValue(0, "PartB_UserInfo"); - var structValueAsDictionary = structValue as IDictionary; - Assert.NotNull(structValueAsDictionary); - Assert.Equal("Me", structValueAsDictionary["UserName"]); - Assert.Equal("Now", structValueAsDictionary["LogTime"]); - - var keyValues = evt.PayloadValue(1, "PartC_Dict"); - var vDict = GetDictionaryFromKeyValueArray(keyValues); - Assert.NotNull(vDict); - Assert.Equal(10, vDict["elem1"]); // Notice they are integers, not strings. - Assert.Equal(20, vDict["elem2"]); + // log a PartB and a dictionary as a PartC + logger.Write("EventWithStringDict_BC", new + { + PartB_UserInfo = new { UserName = "Me", LogTime = "Now" }, + PartC_Dict = dict, + s = "end" + }); + }, + delegate (Event evt) + { + Assert.Equal(logger.Name, evt.ProviderName); + Assert.Equal("EventWithStringDict_BC", evt.EventName); + + var structValue = evt.PayloadValue(0, "PartB_UserInfo"); + var structValueAsDictionary = structValue as IDictionary; + Assert.NotNull(structValueAsDictionary); + Assert.Equal("Me", structValueAsDictionary["UserName"]); + Assert.Equal("Now", structValueAsDictionary["LogTime"]); + + var keyValues = evt.PayloadValue(1, "PartC_Dict"); + var vDict = GetDictionaryFromKeyValueArray(keyValues); + Assert.NotNull(dict); + Assert.Equal("10", vDict["elem1"]); // string values. + Assert.Equal("20", vDict["elem2"]); + + Assert.Equal("end", evt.PayloadValue(2, "s")); + })); + /*************************************************************************/ + tests.Add(new SubTest("Write/Dict/EventWithIntDict_BC", + delegate () + { + // log a Dict as a PartC + logger.Write("EventWithIntDict_BC", new + { + PartB_UserInfo = new { UserName = "Me", LogTime = "Now" }, + PartC_Dict = dictInt, + s = "end" + }); + + }, + delegate (Event evt) + { + Assert.Equal(logger.Name, evt.ProviderName); + Assert.Equal("EventWithIntDict_BC", evt.EventName); + + var structValue = evt.PayloadValue(0, "PartB_UserInfo"); + var structValueAsDictionary = structValue as IDictionary; + Assert.NotNull(structValueAsDictionary); + Assert.Equal("Me", structValueAsDictionary["UserName"]); + Assert.Equal("Now", structValueAsDictionary["LogTime"]); + + var keyValues = evt.PayloadValue(1, "PartC_Dict"); + var vDict = GetDictionaryFromKeyValueArray(keyValues); + Assert.NotNull(vDict); + Assert.Equal(10, vDict["elem1"]); // Notice they are integers, not strings. + Assert.Equal(20, vDict["elem2"]); + + Assert.Equal("end", evt.PayloadValue(2, "s")); + })); + } - Assert.Equal("end", evt.PayloadValue(2, "s")); - })); + /*************************************************************************/ /**************************** Empty Event TESTING ************************/ /*************************************************************************/ @@ -486,7 +499,9 @@ private static void Test_Write_T_In_Manifest_Serialization_Impl( { listener.OnEvent = delegate (Event data) { events.Add(data); }; - listener.EventSourceSynchronousEnable(logger); + listener.EventSourceCommand(logger.Name, EventCommand.Enable); + listener.Start(); + listener.WaitForEventSourceStateChange(logger, true); // Use the Write API. This is OK logger.Write("MyTestEvent", new { arg1 = 3, arg2 = "hi" }); @@ -516,14 +531,14 @@ private void GenerateEnumTest(ref List tests, EventSource logger, T Assert.Equal(logger.Name, evt.ProviderName); Assert.Equal("EnumEvent" + subTestName, evt.EventName); Assert.Equal("start", evt.PayloadValue(0, "b")); - if (evt.IsEtw) + if (!evt.IsEnumValueStronglyTyped(selfDescribing:true, writeEvent:false)) { var value = evt.PayloadValue(1, "v"); Assert.Equal(2, int.Parse(value.ToString())); // Green has the int value of 2. } else { - Assert.Equal(evt.PayloadValue(1, "v"), enumValue); + Assert.Equal(enumValue, evt.PayloadValue(1, "v")); } Assert.Equal("end", evt.PayloadValue(2, "s")); })); diff --git a/src/libraries/System.Diagnostics.Tracing/tests/BasicEventSourceTest/TestsWriteEvent.Etw.cs b/src/libraries/System.Diagnostics.Tracing/tests/BasicEventSourceTest/TestsWriteEvent.Etw.cs index 19c7ea425e5c19..6272dc402382de 100644 --- a/src/libraries/System.Diagnostics.Tracing/tests/BasicEventSourceTest/TestsWriteEvent.Etw.cs +++ b/src/libraries/System.Diagnostics.Tracing/tests/BasicEventSourceTest/TestsWriteEvent.Etw.cs @@ -19,12 +19,11 @@ partial class TestsWriteEvent /// Tests the ETW path. /// [ConditionalFact(nameof(IsProcessElevatedAndNotWindowsNanoServer))] - [ActiveIssue("https://github.com/dotnet/runtime/issues/88305")] public void Test_WriteEvent_Manifest_ETW() { using (var listener = new EtwListener()) { - Test_WriteEvent(listener, false, true); + Test_WriteEvent(listener, false); } } @@ -33,12 +32,11 @@ public void Test_WriteEvent_Manifest_ETW() /// Tests both the ETW and TraceListener paths. /// [ConditionalFact(nameof(IsProcessElevatedAndNotWindowsNanoServer))] - [ActiveIssue("https://github.com/dotnet/runtime/issues/88305")] public void Test_WriteEvent_SelfDescribing_ETW() { using (var listener = new EtwListener()) { - Test_WriteEvent(listener, true, true); + Test_WriteEvent(listener, true); } } @@ -47,7 +45,6 @@ public void Test_WriteEvent_SelfDescribing_ETW() /// Tests the EventListener case /// [ConditionalFact(nameof(IsProcessElevatedAndNotWindowsNanoServer))] - [ActiveIssue("https://github.com/dotnet/runtime/issues/88305")] public void Test_WriteEvent_ComplexData_SelfDescribing_ETW() { using (var listener = new EtwListener()) @@ -62,12 +59,11 @@ public void Test_WriteEvent_ComplexData_SelfDescribing_ETW() /// Tests the EventListener case /// [ConditionalFact(nameof(IsProcessElevatedAndNotWindowsNanoServer))] - [ActiveIssue("https://github.com/dotnet/runtime/issues/88305")] public void Test_WriteEvent_ByteArray_Manifest_ETW() { using (var listener = new EtwListener()) { - Test_WriteEvent_ByteArray(false, listener); + Test_WriteEvent_ByteArray(listener, false); } } @@ -77,59 +73,12 @@ public void Test_WriteEvent_ByteArray_Manifest_ETW() /// Tests the EventListener case /// [ConditionalFact(nameof(IsProcessElevatedAndNotWindowsNanoServer))] - [ActiveIssue("https://github.com/dotnet/runtime/issues/88305")] public void Test_WriteEvent_ByteArray_SelfDescribing_ETW() { using (var listener = new EtwListener()) { - Test_WriteEvent_ByteArray(true, listener); + Test_WriteEvent_ByteArray(listener, true); } } - - static partial void Test_WriteEvent_AddEtwTests(List tests, EventSourceTest logger) - { - if (!PlatformDetection.IsPrivilegedProcess) - { - return; - } - - tests.Add(new SubTest("Write/Basic/EventWithManyTypeArgs", - delegate () - { - logger.EventWithManyTypeArgs("Hello", 1, 2, 3, 'a', 4, 5, 6, 7, - (float)10.0, (double)11.0, logger.Guid); - }, - delegate (Event evt) - { - Assert.Equal(logger.Name, evt.ProviderName); - Assert.Equal("EventWithManyTypeArgs", evt.EventName); - Assert.Equal("Hello", evt.PayloadValue(0, "msg")); - Assert.Equal((float)10.0, evt.PayloadValue(9, "f")); - Assert.Equal((double)11.0, evt.PayloadValue(10, "d")); - Assert.Equal(logger.Guid, evt.PayloadValue(11, "guid")); - })); - - tests.Add(new SubTest("Write/Activity/EventWithXferWeirdArgs", - delegate () - { - var actid = Guid.NewGuid(); - logger.EventWithXferWeirdArgs(actid, - (IntPtr)128, - true, - SdtEventSources.MyLongEnum.LongVal1); - }, - delegate (Event evt) - { - Assert.Equal(logger.Name, evt.ProviderName); - - // We log EventWithXferWeirdArgs in one case and - // WorkWeirdArgs/Send in the other - Assert.Contains("WeirdArgs", evt.EventName); - - Assert.Equal("128", evt.PayloadValue(0, "iptr").ToString()); - Assert.True((bool)evt.PayloadValue(1, "b")); - Assert.Equal((long)SdtEventSources.MyLongEnum.LongVal1, ((IConvertible)evt.PayloadValue(2, "le")).ToInt64(null)); - })); - } } } diff --git a/src/libraries/System.Diagnostics.Tracing/tests/BasicEventSourceTest/TestsWriteEvent.cs b/src/libraries/System.Diagnostics.Tracing/tests/BasicEventSourceTest/TestsWriteEvent.cs index 161ad458edfe4d..ae0d1602e8407c 100644 --- a/src/libraries/System.Diagnostics.Tracing/tests/BasicEventSourceTest/TestsWriteEvent.cs +++ b/src/libraries/System.Diagnostics.Tracing/tests/BasicEventSourceTest/TestsWriteEvent.cs @@ -11,57 +11,33 @@ namespace BasicEventSourceTests { public partial class TestsWriteEvent { - /// - /// Tests WriteEvent using the manifest based mechanism. - /// Tests bTraceListener path. - /// - [Fact] - [ActiveIssue("https://github.com/dotnet/runtime/issues/21295", TargetFrameworkMonikers.NetFramework)] - public void Test_WriteEvent_Manifest_EventListener() + + public static TheoryData GetListenerConfigurations() { - using (var listener = new EventListenerListener()) + TheoryData data = new TheoryData(); + foreach(var listener in GetListeners()) { - Test_WriteEvent(listener, false); + data.Add(listener, /*self-describing*/ false); } - } - - /// - /// Tests WriteEvent using the manifest based mechanism. - /// Tests bTraceListener path using events instead of virtual callbacks. - /// - [Fact] - [ActiveIssue("https://github.com/dotnet/runtime/issues/21295", TargetFrameworkMonikers.NetFramework)] - public void Test_WriteEvent_Manifest_EventListener_UseEvents() - { - Listener listener = new EventListenerListener(true); - Test_WriteEvent(listener, false); - } - - /// - /// Tests WriteEvent using the self-describing mechanism. - /// Tests both the ETW and TraceListener paths. - /// - [Fact] - [ActiveIssue("https://github.com/dotnet/runtime/issues/21295", TargetFrameworkMonikers.NetFramework)] - public void Test_WriteEvent_SelfDescribing_EventListener() - { - using (var listener = new EventListenerListener()) + // listener objects are used once and then disposed, so we need to create a 2nd set + foreach (var listener in GetListeners()) { - Test_WriteEvent(listener, true); + data.Add(listener, /*self-describing*/ true); } + return data; } - /// - /// Tests WriteEvent using the self-describing mechanism. - /// Tests both the ETW and TraceListener paths using events - /// instead of virtual callbacks. - /// - [Fact] - [ActiveIssue("https://github.com/dotnet/runtime/issues/21295", TargetFrameworkMonikers.NetFramework)] - public void Test_WriteEvent_SelfDescribing_EventListener_UseEvents() + public static TheoryData GetListeners() { - Listener listener = new EventListenerListener(true); - Test_WriteEvent(listener, true); + TheoryData data = new TheoryData(); + if (PlatformDetection.IsNetCore && PlatformDetection.IsNotAndroid && PlatformDetection.IsNotBrowser && + (PlatformDetection.IsNotMonoRuntime || PlatformDetection.IsMacCatalyst)) + { + data.Add(new EventPipeListener()); + } + data.Add(new EventListenerListener()); + data.Add(new EventListenerListener(true)); + return data; } [Fact] @@ -89,10 +65,19 @@ public void Test_WriteEvent_NoAttribute() } } - /// - /// Helper method for the two tests above. - /// - private void Test_WriteEvent(Listener listener, bool useSelfDescribingEvents, bool isEtwListener = false) + [Theory] + [MemberData(nameof(GetListenerConfigurations))] + [ActiveIssue("https://github.com/dotnet/runtime/issues/21295", TargetFrameworkMonikers.NetFramework)] + public void Test_WriteEvent(Listener listener, bool useSelfDescribingEvents) + { + using (listener) + { + Test_WriteEvent_Helper(listener, useSelfDescribingEvents); + } + } + + + private void Test_WriteEvent_Helper(Listener listener, bool useSelfDescribingEvents) { using (var logger = new EventSourceTest(useSelfDescribingEvents)) { @@ -147,7 +132,48 @@ private void Test_WriteEvent(Listener listener, bool useSelfDescribingEvents, bo Assert.Equal("s8", (string)evt.PayloadValue(8, "s8")); })); /*************************************************************************/ - Test_WriteEvent_AddEtwTests(tests, logger); + + tests.Add(new SubTest("Write/Basic/EventWithManyTypeArgs", + delegate () + { + logger.EventWithManyTypeArgs("Hello", 1, 2, 3, 'a', 4, 5, 6, 7, + (float)10.0, (double)11.0, logger.Guid); + }, + delegate (Event evt) + { + Assert.Equal(logger.Name, evt.ProviderName); + Assert.Equal("EventWithManyTypeArgs", evt.EventName); + Assert.Equal("Hello", evt.PayloadValue(0, "msg")); + Assert.Equal((float)10.0, evt.PayloadValue(9, "f")); + Assert.Equal((double)11.0, evt.PayloadValue(10, "d")); + Assert.Equal(logger.Guid, evt.PayloadValue(11, "guid")); + })); + + if (!listener.IsEventPipe) // EventPipe doesn't encode this correctly + { + tests.Add(new SubTest("Write/Activity/EventWithXferWeirdArgs", + delegate () + { + var actid = Guid.NewGuid(); + logger.EventWithXferWeirdArgs(actid, + (IntPtr)128, + true, + SdtEventSources.MyLongEnum.LongVal1); + }, + delegate (Event evt) + { + Assert.Equal(logger.Name, evt.ProviderName); + + // We log EventWithXferWeirdArgs in one case and + // WorkWeirdArgs/Send in the other + Assert.Contains("WeirdArgs", evt.EventName); + + Assert.Equal("128", evt.PayloadValue(0, "iptr").ToString()); + Assert.True((bool)evt.PayloadValue(1, "b")); + Assert.Equal((long)SdtEventSources.MyLongEnum.LongVal1, ((IConvertible)evt.PayloadValue(2, "le")).ToInt64(null)); + })); + } + /*************************************************************************/ /*************************** ENUM TESTING *******************************/ @@ -165,7 +191,7 @@ private void Test_WriteEvent(Listener listener, bool useSelfDescribingEvents, bo Assert.Equal("EventEnum", evt.EventName); Assert.Equal(1, ((IConvertible)evt.PayloadValue(0, "x")).ToInt32(null)); - if (evt.IsEtw && !useSelfDescribingEvents) + if (evt.IsEnumValueStronglyTyped(useSelfDescribingEvents, writeEvent:true)) Assert.Equal("Blue", evt.PayloadString(0, "x")); })); @@ -180,7 +206,7 @@ private void Test_WriteEvent(Listener listener, bool useSelfDescribingEvents, bo Assert.Equal("EventEnum1", evt.EventName); Assert.Equal(1, ((IConvertible)evt.PayloadValue(0, "x")).ToInt32(null)); - if (evt.IsEtw && !useSelfDescribingEvents) + if (evt.IsEnumValueStronglyTyped(useSelfDescribingEvents, writeEvent: true)) Assert.Equal("Blue", evt.PayloadString(0, "x")); })); @@ -298,8 +324,8 @@ private void Test_WriteEvent(Listener listener, bool useSelfDescribingEvents, bo Assert.Equal("", evt.PayloadValue(2, null)); })); - // Self-describing ETW does not support NULL arguments. - if (useSelfDescribingEvents && !(isEtwListener)) + // Self-describing ETW+EventPipe do not support NULL arguments. + if (useSelfDescribingEvents && listener.IsSelfDescribingNullArgSupported) { tests.Add(new SubTest("WriteEvent/Basic/EventVarArgsWithString", delegate () @@ -353,23 +379,19 @@ private void Test_WriteEvent(Listener listener, bool useSelfDescribingEvents, bo } } - static partial void Test_WriteEvent_AddEtwTests(List tests, EventSourceTest logger); - /**********************************************************************/ - /// - /// Tests sending complex data (class, arrays etc) from WriteEvent - /// Tests the EventListener case - /// - [Fact] - public void Test_WriteEvent_ComplexData_SelfDescribing_EventListener() + + [Theory] + [MemberData(nameof(GetListeners))] + public void Test_WriteEvent_ComplexData_SelfDescribing(Listener listener) { - using (var listener = new EventListenerListener()) + using (listener) { - Test_WriteEvent_ComplexData_SelfDescribing(listener); + Test_WriteEvent_ComplexData_SelfDescribing_Helper(listener); } } - private void Test_WriteEvent_ComplexData_SelfDescribing(Listener listener) + private void Test_WriteEvent_ComplexData_SelfDescribing_Helper(Listener listener) { using (var logger = new EventSourceTestSelfDescribingOnly()) { @@ -391,7 +413,9 @@ private void Test_WriteEvent_ComplexData_SelfDescribing(Listener listener) Assert.Equal(5, evt.PayloadValue(1, "anInt")); })); - tests.Add(new SubTest("WriteEvent/SelfDescribingOnly/UserData", + if(!listener.IsEventPipe) // EventPipe doesn't correctly encode metadata for this + { + tests.Add(new SubTest("WriteEvent/SelfDescribingOnly/UserData", delegate () { logger.EventUserDataInt(new UserData() { x = 3, y = 8 }, 5); @@ -406,71 +430,74 @@ private void Test_WriteEvent_ComplexData_SelfDescribing(Listener listener) Assert.Equal(8, (int)aClass["y"]); Assert.Equal(5, evt.PayloadValue(1, "anInt")); })); + } + if (listener.IsSelfDescribingNullableSupported) + { + int? nullableInt = 12; + tests.Add(new SubTest("WriteEvent/SelfDescribingOnly/Int12", + delegate () + { + logger.EventNullableIntInt(nullableInt, 5); + }, + delegate (Event evt) + { + Assert.Equal(logger.Name, evt.ProviderName); + Assert.Equal("EventNullableIntInt", evt.EventName); - int? nullableInt = 12; - tests.Add(new SubTest("WriteEvent/SelfDescribingOnly/Int12", - delegate () - { - logger.EventNullableIntInt(nullableInt, 5); - }, - delegate (Event evt) - { - Assert.Equal(logger.Name, evt.ProviderName); - Assert.Equal("EventNullableIntInt", evt.EventName); - - var payload = evt.PayloadValue(0, "nullableInt"); - Assert.Equal(nullableInt, TestUtilities.UnwrapNullable(payload)); - Assert.Equal(5, evt.PayloadValue(1, "anInt")); - })); + var payload = evt.PayloadValue(0, "nullableInt"); + Assert.Equal(nullableInt, TestUtilities.UnwrapNullable(payload)); + Assert.Equal(5, evt.PayloadValue(1, "anInt")); + })); - int? nullableInt2 = null; - tests.Add(new SubTest("WriteEvent/SelfDescribingOnly/IntNull", - delegate () - { - logger.EventNullableIntInt(nullableInt2, 5); - }, - delegate (Event evt) - { - Assert.Equal(logger.Name, evt.ProviderName); - Assert.Equal("EventNullableIntInt", evt.EventName); + int? nullableInt2 = null; + tests.Add(new SubTest("WriteEvent/SelfDescribingOnly/IntNull", + delegate () + { + logger.EventNullableIntInt(nullableInt2, 5); + }, + delegate (Event evt) + { + Assert.Equal(logger.Name, evt.ProviderName); + Assert.Equal("EventNullableIntInt", evt.EventName); - var payload = evt.PayloadValue(0, "nullableInt"); - Assert.Equal(nullableInt2, TestUtilities.UnwrapNullable(payload)); - Assert.Equal(5, evt.PayloadValue(1, "anInt")); - })); + var payload = evt.PayloadValue(0, "nullableInt"); + Assert.Equal(nullableInt2, TestUtilities.UnwrapNullable(payload)); + Assert.Equal(5, evt.PayloadValue(1, "anInt")); + })); - DateTime? nullableDate = DateTime.Now; - tests.Add(new SubTest("WriteEvent/SelfDescribingOnly/DateTimeNow", - delegate () - { - logger.EventNullableDateTimeInt(nullableDate, 5); - }, - delegate (Event evt) - { - Assert.Equal(logger.Name, evt.ProviderName); - Assert.Equal("EventNullableDateTimeInt", evt.EventName); + DateTime? nullableDate = DateTime.Now; + tests.Add(new SubTest("WriteEvent/SelfDescribingOnly/DateTimeNow", + delegate () + { + logger.EventNullableDateTimeInt(nullableDate, 5); + }, + delegate (Event evt) + { + Assert.Equal(logger.Name, evt.ProviderName); + Assert.Equal("EventNullableDateTimeInt", evt.EventName); - var payload = evt.PayloadValue(0, "nullableDate"); - Assert.Equal(nullableDate, TestUtilities.UnwrapNullable(payload)); - Assert.Equal(5, evt.PayloadValue(1, "anInt")); - })); + var payload = evt.PayloadValue(0, "nullableDate"); + Assert.Equal(nullableDate, TestUtilities.UnwrapNullable(payload)); + Assert.Equal(5, evt.PayloadValue(1, "anInt")); + })); - DateTime? nullableDate2 = null; - tests.Add(new SubTest("WriteEvent/SelfDescribingOnly/DateTimeNull", - delegate () - { - logger.EventNullableDateTimeInt(nullableDate2, 5); - }, - delegate (Event evt) - { - Assert.Equal(logger.Name, evt.ProviderName); - Assert.Equal("EventNullableDateTimeInt", evt.EventName); + DateTime? nullableDate2 = null; + tests.Add(new SubTest("WriteEvent/SelfDescribingOnly/DateTimeNull", + delegate () + { + logger.EventNullableDateTimeInt(nullableDate2, 5); + }, + delegate (Event evt) + { + Assert.Equal(logger.Name, evt.ProviderName); + Assert.Equal("EventNullableDateTimeInt", evt.EventName); - var payload = evt.PayloadValue(0, "nullableDate"); - Assert.Equal(nullableDate2, TestUtilities.UnwrapNullable(nullableDate2)); - Assert.Equal(5, evt.PayloadValue(1, "anInt")); - })); + var payload = evt.PayloadValue(0, "nullableDate"); + Assert.Equal(nullableDate2, TestUtilities.UnwrapNullable(nullableDate2)); + Assert.Equal(5, evt.PayloadValue(1, "anInt")); + })); + } // If you only wish to run one or several of the tests you can filter them here by // Uncommenting the following line. @@ -481,48 +508,17 @@ private void Test_WriteEvent_ComplexData_SelfDescribing(Listener listener) } } - /**********************************************************************/ - /// - /// Tests sending complex data (class, arrays etc) from WriteEvent - /// Uses Manifest format - /// Tests the EventListener case - /// - [Fact] - public void Test_WriteEvent_ByteArray_Manifest_EventListener() + [Theory] + [MemberData(nameof(GetListenerConfigurations))] + private void Test_WriteEvent_ByteArray(Listener listener, bool useSelfDescribingEvents) { - using (var listener = new EventListenerListener()) + using(listener) { - Test_WriteEvent_ByteArray(false, listener); + Test_WriteEvent_ByteArray_Helper(listener, useSelfDescribingEvents); } } - /// - /// Tests sending complex data (class, arrays etc) from WriteEvent - /// Uses Manifest format - /// Tests the EventListener case using events instead of virtual - /// callbacks. - /// - [Fact] - public void Test_WriteEvent_ByteArray_Manifest_EventListener_UseEvents() - { - Test_WriteEvent_ByteArray(false, new EventListenerListener(true)); - } - - /// - /// Tests sending complex data (class, arrays etc) from WriteEvent - /// Uses Self-Describing format - /// Tests the EventListener case - /// - [Fact] - public void Test_WriteEvent_ByteArray_SelfDescribing_EventListener() - { - using (var listener = new EventListenerListener()) - { - Test_WriteEvent_ByteArray(true, listener); - } - } - - private void Test_WriteEvent_ByteArray(bool useSelfDescribingEvents, Listener listener) + private void Test_WriteEvent_ByteArray_Helper(Listener listener, bool useSelfDescribingEvents) { EventSourceSettings settings = EventSourceSettings.EtwManifestEventFormat; if (useSelfDescribingEvents) @@ -558,55 +554,61 @@ private void Test_WriteEvent_ByteArray(bool useSelfDescribingEvents, Listener li if (!useSelfDescribingEvents) { - /*************************************************************************/ - tests.Add(new SubTest("Write/Array/NonEventCallingEventWithBytePtrArg", + if (!listener.IsEventPipe) // EventPipe doesn't correctly encode metadata for this + { + /*************************************************************************/ + tests.Add(new SubTest("Write/Array/NonEventCallingEventWithBytePtrArg", + delegate () + { + logger.NonEventCallingEventWithBytePtrArg(blob, 2, 4, 1001); + }, + delegate (Event evt) + { + Assert.Equal(logger.Name, evt.ProviderName); + Assert.Equal("EventWithBytePtrArg", evt.EventName); + + if (evt.IsSizeAndPointerCoallescedIntoSingleArg) + { + Assert.Equal(2, evt.PayloadCount); + byte[] retBlob = (byte[])evt.PayloadValue(0, "blob"); + Assert.Equal(4, retBlob.Length); + Assert.Equal(retBlob[0], blob[2]); + Assert.Equal(retBlob[3], blob[2 + 3]); + Assert.Equal(1001, (int)evt.PayloadValue(1, "n")); + } + else + { + Assert.Equal(3, evt.PayloadCount); + byte[] retBlob = (byte[])evt.PayloadValue(1, "blob"); + Assert.Equal(4, retBlob.Length); + Assert.Equal(retBlob[0], blob[2]); + Assert.Equal(retBlob[3], blob[2 + 3]); + Assert.Equal(1001, (int)evt.PayloadValue(2, "n")); + } + })); + } + } + + if (!listener.IsEventPipe) // EventPipe doesn't correctly encode metadata for this + { + tests.Add(new SubTest("Write/Array/EventWithLongByteArray", delegate () { - logger.NonEventCallingEventWithBytePtrArg(blob, 2, 4, 1001); + logger.EventWithLongByteArray(blob, 1000); }, delegate (Event evt) { Assert.Equal(logger.Name, evt.ProviderName); - Assert.Equal("EventWithBytePtrArg", evt.EventName); + Assert.Equal("EventWithLongByteArray", evt.EventName); - if (evt.IsEtw) - { - Assert.Equal(2, evt.PayloadCount); - byte[] retBlob = (byte[])evt.PayloadValue(0, "blob"); - Assert.Equal(4, retBlob.Length); - Assert.Equal(retBlob[0], blob[2]); - Assert.Equal(retBlob[3], blob[2 + 3]); - Assert.Equal(1001, (int)evt.PayloadValue(1, "n")); - } - else - { - Assert.Equal(3, evt.PayloadCount); - byte[] retBlob = (byte[])evt.PayloadValue(1, "blob"); - Assert.Equal(4, retBlob.Length); - Assert.Equal(retBlob[0], blob[2]); - Assert.Equal(retBlob[3], blob[2 + 3]); - Assert.Equal(1001, (int)evt.PayloadValue(2, "n")); - } + Assert.Equal(2, evt.PayloadCount); + byte[] retBlob = (byte[])evt.PayloadValue(0, "blob"); + Assert.True(Equal(blob, retBlob)); + + Assert.Equal(1000, (long)evt.PayloadValue(1, "lng")); })); } - tests.Add(new SubTest("Write/Array/EventWithLongByteArray", - delegate () - { - logger.EventWithLongByteArray(blob, 1000); - }, - delegate (Event evt) - { - Assert.Equal(logger.Name, evt.ProviderName); - Assert.Equal("EventWithLongByteArray", evt.EventName); - - Assert.Equal(2, evt.PayloadCount); - byte[] retBlob = (byte[])evt.PayloadValue(0, "blob"); - Assert.True(Equal(blob, retBlob)); - - Assert.Equal(1000, (long)evt.PayloadValue(1, "lng")); - })); - /* TODO: NULL byte array does not seem to be supported. * An EventSourceMessage event is written for this case. tests.Add(new SubTest("Write/Array/EventWithNullByteArray", @@ -628,7 +630,9 @@ private void Test_WriteEvent_ByteArray(bool useSelfDescribingEvents, Listener li })); */ - tests.Add(new SubTest("Write/Array/EventWithEmptyByteArray", + if (!listener.IsEventPipe) // EventPipe doesn't correctly encode metadata for this + { + tests.Add(new SubTest("Write/Array/EventWithEmptyByteArray", delegate () { logger.EventWithByteArrayArg(Array.Empty(), 0); @@ -644,6 +648,7 @@ private void Test_WriteEvent_ByteArray(bool useSelfDescribingEvents, Listener li Assert.Equal(0, (int)evt.PayloadValue(1, "n")); })); + } // If you only wish to run one or several of the tests you can filter them here by // Uncommenting the following line. diff --git a/src/libraries/System.Diagnostics.Tracing/tests/BasicEventSourceTest/TestsWriteEventToListener.cs b/src/libraries/System.Diagnostics.Tracing/tests/BasicEventSourceTest/TestsWriteEventToListener.cs index 36607510b9c1ce..b709142311ecb9 100644 --- a/src/libraries/System.Diagnostics.Tracing/tests/BasicEventSourceTest/TestsWriteEventToListener.cs +++ b/src/libraries/System.Diagnostics.Tracing/tests/BasicEventSourceTest/TestsWriteEventToListener.cs @@ -334,6 +334,7 @@ public void Test_EventSourceCreatedEvents_BeforeListener() using (var listener = new EventListenerListener()) { + listener.Start(); List eventSourceNotificationsReceived = new List(); listener.EventSourceCreated += (s, a) => { @@ -388,6 +389,7 @@ public void Test_EventSourceCreatedEvents_AfterListener() { using (var listener = new EventListenerListener()) { + listener.Start(); string esName = "EventSourceName_HopefullyUnique"; string esName2 = "EventSourceName_HopefullyUnique2"; bool esNameHit = false; @@ -448,6 +450,7 @@ public void Test_EventListenerThrows_ExceptionIsNotRethrownToCaller(bool setThro { using (var listener = new EventListenerListener()) { + listener.Start(); listener.EventSourceSynchronousEnable(log); var thrownException = new Exception("Oops"); diff --git a/src/libraries/System.Diagnostics.Tracing/tests/System.Diagnostics.Tracing.Tests.csproj b/src/libraries/System.Diagnostics.Tracing/tests/System.Diagnostics.Tracing.Tests.csproj index 8f5c514373fac2..b999bb5d9ae733 100644 --- a/src/libraries/System.Diagnostics.Tracing/tests/System.Diagnostics.Tracing.Tests.csproj +++ b/src/libraries/System.Diagnostics.Tracing/tests/System.Diagnostics.Tracing.Tests.csproj @@ -1,4 +1,4 @@ - + true $(NetCoreAppCurrent)-windows;$(NetCoreAppCurrent)-browser;$(NetCoreAppCurrent) @@ -27,6 +27,7 @@ + @@ -54,5 +55,6 @@ + \ No newline at end of file