diff --git a/src/coreclr/tests/src/tracing/regress/GitHub_34839/GitHub_34839.cs b/src/coreclr/tests/src/tracing/regress/GitHub_34839/GitHub_34839.cs new file mode 100644 index 00000000000000..7b0a978153739d --- /dev/null +++ b/src/coreclr/tests/src/tracing/regress/GitHub_34839/GitHub_34839.cs @@ -0,0 +1,238 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.Diagnostics.Tracing; +using System.IO; +using System.Reflection; +using System.Threading; + +/* + +This test is a regression test for github.com/dotnet/runtime/issues/34839. + +We test whether it's possible to enable many sessions via reflection on EventPipe.Enable method, and whether they can be disabled by calling corresponding Disable() method. + +*/ +namespace EventPipe.Issue34839 +{ + public enum EventPipeSerializationFormat + { + NetPerf, + NetTrace + } + + public sealed class TraceConfiguration + { + private ConstructorInfo m_configurationCtor; + private MethodInfo m_enableProviderMethod; + private MethodInfo m_setProfilerSamplingRateMethod; + + private object m_configurationObject; + + public TraceConfiguration( + string outputFile, + uint circularBufferMB) + { + // Initialize reflection references. + if (!Initialize()) + { + throw new InvalidOperationException("Reflection failed."); + } + + m_configurationObject = m_configurationCtor.Invoke( + new object[] + { + outputFile, + EventPipeSerializationFormat.NetTrace, + circularBufferMB + }); + } + + public void EnableProvider( + string providerName, + UInt64 keywords, + uint level) + { + m_enableProviderMethod.Invoke( + m_configurationObject, + new object[] + { + providerName, + keywords, + level + }); + } + + internal object ConfigurationObject + { + get { return m_configurationObject; } + } + + public void SetSamplingRate(TimeSpan minDelayBetweenSamples) + { + m_setProfilerSamplingRateMethod.Invoke( + m_configurationObject, + new object[] + { + minDelayBetweenSamples + }); + } + + private bool Initialize() + { + Assembly SPC = typeof(System.Diagnostics.Tracing.EventSource).Assembly; + if (SPC == null) + { + Console.WriteLine("System.Private.CoreLib assembly == null"); + return false; + } + + Type configurationType = SPC.GetType("System.Diagnostics.Tracing.EventPipeConfiguration"); + if (configurationType == null) + { + Console.WriteLine("configurationType == null"); + return false; + } + Type formatType = SPC.GetType("System.Diagnostics.Tracing.EventPipeSerializationFormat"); + if (formatType == null) + { + Console.WriteLine("formatType == null"); + return false; + } + + m_configurationCtor = configurationType.GetConstructor( + BindingFlags.NonPublic | BindingFlags.Instance, + null, + new Type[] { typeof(string), formatType, typeof(uint) }, + null); + if (m_configurationCtor == null) + { + Console.WriteLine("configurationCtor == null"); + return false; + } + + m_enableProviderMethod = configurationType.GetMethod( + "EnableProvider", + BindingFlags.NonPublic | BindingFlags.Instance); + if (m_enableProviderMethod == null) + { + Console.WriteLine("enableProviderMethod == null"); + return false; + } + + m_setProfilerSamplingRateMethod = configurationType.GetMethod( + "SetProfilerSamplingRate", + BindingFlags.NonPublic | BindingFlags.Instance); + if (m_setProfilerSamplingRateMethod == null) + { + Console.WriteLine("setProfilerSamplingRate == null"); + return false; + } + + return true; + } + } + + class Program + { + private static MethodInfo m_enableMethod; + private static MethodInfo m_disableMethod; + + public static void Enable(TraceConfiguration traceConfig) + { + m_enableMethod.Invoke( + null, + new object[] + { + traceConfig.ConfigurationObject + }); + } + + public static void Disable() + { + m_disableMethod.Invoke( + null, + null); + } + + static int Main(string[] args) + { + TimeSpan profSampleDelay = TimeSpan.FromMilliseconds(1); + + Assembly SPC = typeof(System.Diagnostics.Tracing.EventSource).Assembly; + Type eventPipeType = SPC.GetType("System.Diagnostics.Tracing.EventPipe"); + m_enableMethod = eventPipeType.GetMethod("Enable", BindingFlags.NonPublic | BindingFlags.Static); + m_disableMethod = eventPipeType.GetMethod("Disable", BindingFlags.NonPublic | BindingFlags.Static); + + // Setup the configuration values. + uint circularBufferMB = 1024; // 1 GB + uint level = 5; // Verbose + + const uint configurationCnt = 5; + + // Create 5 instances of EventPipeConfiguration. + TraceConfiguration[] configs = new TraceConfiguration[configurationCnt]; + + for (int i = 0; i < configurationCnt; i++) + { + string outputFile = $"default{i}.nettrace"; + configs[i] = new TraceConfiguration(outputFile, circularBufferMB); + + // Setup the provider values. + // Public provider. + string providerName = "Microsoft-Windows-DotNETRuntime"; + UInt64 keywords = 0x4c14fccbd; + + // Enable the provider. + configs[i].EnableProvider(providerName, keywords, level); + + // Private provider. + providerName = "Microsoft-Windows-DotNETRuntimePrivate"; + keywords = 0x4002000b; + + // Enable the provider. + configs[i].EnableProvider(providerName, keywords, level); + + // Sample profiler. + providerName = "Microsoft-DotNETCore-SampleProfiler"; + keywords = 0x0; + + // Enable the provider. + configs[i].EnableProvider(providerName, keywords, level); + + // Set the sampling rate. + configs[i].SetSamplingRate(profSampleDelay); + } + + // Enable tracing many times + for (int i = 0; i < configurationCnt; i++) + { + Enable(configs[i]); + } + + // Sleep for 5 seconds to let some events to be written + Thread.Sleep(5000); + + // Disable + for (int i = 0; i < configurationCnt; i++) + { + Disable(); + } + + // Assert that all trace files exist now. + for (int i = 0; i < configurationCnt; i++) + { + string outputFile = $"default{i}.nettrace"; + if (!File.Exists(outputFile)) + { + Console.WriteLine($"The expected output file {i} does not exist"); + return 1; + } + } + + return 100; + } + } +} diff --git a/src/coreclr/tests/src/tracing/regress/GitHub_34839/GitHub_34839.csproj b/src/coreclr/tests/src/tracing/regress/GitHub_34839/GitHub_34839.csproj new file mode 100644 index 00000000000000..c6c03714e2f0b0 --- /dev/null +++ b/src/coreclr/tests/src/tracing/regress/GitHub_34839/GitHub_34839.csproj @@ -0,0 +1,14 @@ + + + Exe + BuildAndRun + true + 0 + + true + true + + + + + diff --git a/src/libraries/System.Private.CoreLib/src/System/Diagnostics/Tracing/EventPipe.cs b/src/libraries/System.Private.CoreLib/src/System/Diagnostics/Tracing/EventPipe.cs index 308c19ada6281b..1abbf006e57b96 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Diagnostics/Tracing/EventPipe.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Diagnostics/Tracing/EventPipe.cs @@ -2,6 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +using System.Collections.Concurrent; using System.Collections.Generic; using System.Runtime.InteropServices; using System.Threading; @@ -177,7 +178,7 @@ internal void SetProfilerSamplingRate(TimeSpan minTimeBetweenSamples) internal static class EventPipe { - private static ulong s_sessionID = 0; + internal static ConcurrentQueue s_sessionIDs = new ConcurrentQueue(); internal static void Enable(EventPipeConfiguration configuration) { @@ -193,16 +194,19 @@ internal static void Enable(EventPipeConfiguration configuration) EventPipeProviderConfiguration[] providers = configuration.Providers; - s_sessionID = EventPipeInternal.Enable( + s_sessionIDs.Enqueue(EventPipeInternal.Enable( configuration.OutputFile, configuration.Format, configuration.CircularBufferSizeInMB, - providers); + providers)); } internal static void Disable() { - EventPipeInternal.Disable(s_sessionID); + if (s_sessionIDs.TryDequeue(out ulong sessionID)) + { + EventPipeInternal.Disable(sessionID); + } } }