Add EventPipe events test (#659)
authorjiangzeng01 <49379637+jiangzeng01@users.noreply.github.com>
Wed, 11 Dec 2019 01:37:20 +0000 (09:37 +0800)
committerJohn Salem <josalem@microsoft.com>
Wed, 11 Dec 2019 01:37:20 +0000 (17:37 -0800)
src/tests/eventpipe/ContentionEvents.cs [new file with mode: 0644]
src/tests/eventpipe/GCEvents.cs [new file with mode: 0644]
src/tests/eventpipe/LoaderEvents.cs [new file with mode: 0644]
src/tests/eventpipe/MethodEvents.cs [new file with mode: 0644]
src/tests/eventpipe/ThreadPoolEvents.cs [new file with mode: 0644]

diff --git a/src/tests/eventpipe/ContentionEvents.cs b/src/tests/eventpipe/ContentionEvents.cs
new file mode 100644 (file)
index 0000000..e904a6e
--- /dev/null
@@ -0,0 +1,90 @@
+// 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 Xunit;
+using Xunit.Abstractions;
+using System.Threading;
+using Microsoft.Diagnostics.Tools.RuntimeClient;
+using System.Collections.Generic;
+using System.Diagnostics.Tracing;
+using EventPipe.UnitTests.Common;
+using Microsoft.Diagnostics.Tracing;
+
+namespace EventPipe.UnitTests.ContentionValidation
+{
+
+    public class TestClass{
+        public int a;
+        public void DoSomething(TestClass obj)
+        {
+            lock(obj)
+            {
+                obj.a = 3;
+                Thread.Sleep(100);
+            }
+        } 
+    }
+    public class ProviderTests
+    {
+        private readonly ITestOutputHelper output;
+
+        public ProviderTests(ITestOutputHelper outputHelper)
+        {
+            output = outputHelper;
+        }
+
+        [Fact]
+        public async void Contention_ProducesEvents()
+        {
+            await RemoteTestExecutorHelper.RunTestCaseAsync(() => 
+            {
+                Dictionary<string, ExpectedEventCount> _expectedEventCounts = new Dictionary<string, ExpectedEventCount>()
+                {
+                    { "Microsoft-Windows-DotNETRuntimeRundown", -1 }
+                };
+
+                var providers = new List<Provider>()
+                {
+                    //ContentionKeyword (0x4000): 0b100_0000_0000_0000                
+                    new Provider("Microsoft-Windows-DotNETRuntime", 0b100_0000_0000_0000, EventLevel.Informational)
+                };
+
+                Action _eventGeneratingAction = () => 
+                {
+                    for (int i = 0; i < 50; i++)
+                    {
+                        if (i % 10 == 0)
+                            Logger.logger.Log($"Thread lock occured {i} times...");
+                        var myobject = new TestClass();
+                        Thread thread1 = new Thread(new ThreadStart(() => myobject.DoSomething(myobject)));
+                        Thread thread2 = new Thread(new ThreadStart(() => myobject.DoSomething(myobject)));
+                        thread1.Start();
+                        thread2.Start();
+                        thread1.Join();
+                        thread2.Join();
+                    }
+                };
+
+                Func<EventPipeEventSource, Func<int>> _DoesTraceContainEvents = (source) => 
+                {
+                    int ContentionStartEvents = 0;
+                    source.Clr.ContentionStart += (eventData) => ContentionStartEvents += 1;
+                    int ContentionStopEvents = 0;
+                    source.Clr.ContentionStop += (eventData) => ContentionStopEvents += 1;
+                    return () => {
+                        Logger.logger.Log("Event counts validation");
+                        Logger.logger.Log("ContentionStartEvents: " + ContentionStartEvents);
+                        Logger.logger.Log("ContentionStopEvents: " + ContentionStopEvents);
+                        return ContentionStartEvents >= 50 && ContentionStopEvents >= 50 ? 100 : -1;
+                    };
+                };
+                var config = new SessionConfiguration(circularBufferSizeMB: (uint)Math.Pow(2, 10), format: EventPipeSerializationFormat.NetTrace,  providers: providers);
+
+                var ret = IpcTraceTest.RunAndValidateEventCounts(_expectedEventCounts, _eventGeneratingAction, config, _DoesTraceContainEvents);
+                Assert.Equal(100, ret);
+            }, output);
+        }
+    }
+}
\ No newline at end of file
diff --git a/src/tests/eventpipe/GCEvents.cs b/src/tests/eventpipe/GCEvents.cs
new file mode 100644 (file)
index 0000000..990d9d6
--- /dev/null
@@ -0,0 +1,252 @@
+// 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 Xunit;
+using Xunit.Abstractions;
+using Microsoft.Diagnostics.Tools.RuntimeClient;
+using System.Collections.Generic;
+using System.Diagnostics.Tracing;
+using EventPipe.UnitTests.Common;
+using Microsoft.Diagnostics.Tracing;
+using System.Threading;
+
+namespace EventPipe.UnitTests.GCEventsValidation
+{
+
+    public class TestClass
+    {
+        public int a;
+        public string b;
+
+        public TestClass()
+        {
+            a = 0;
+            b = "";
+        }
+    }
+    public class ProviderTests
+    {
+        private readonly ITestOutputHelper output;
+
+        public ProviderTests(ITestOutputHelper outputHelper)
+        {
+            output = outputHelper;
+        }
+
+        [Fact]
+        public async void GCCollect_ProducesEvents()
+        {
+            await RemoteTestExecutorHelper.RunTestCaseAsync(() => 
+            {
+                Dictionary<string, ExpectedEventCount> _expectedEventCounts = new Dictionary<string, ExpectedEventCount>()
+                {
+                    { "Microsoft-Windows-DotNETRuntime", -1 },
+                    { "Microsoft-Windows-DotNETRuntimeRundown", -1 },
+                    { "Microsoft-DotNETCore-SampleProfiler", -1 }
+                };
+
+                var providers = new List<Provider>()
+                {
+                    new Provider("Microsoft-DotNETCore-SampleProfiler"),
+                    //GCKeyword (0x1): 0b1
+                    new Provider("Microsoft-Windows-DotNETRuntime", 0b1, EventLevel.Informational)
+                };
+
+                Action _eventGeneratingAction = () => 
+                {
+                    for (int i = 0; i < 50; i++)
+                    {
+                        if (i % 10 == 0)
+                            Logger.logger.Log($"Called GC.Collect() {i} times...");
+                        TestClass testClass = new TestClass();
+                        testClass = null;
+                        GC.Collect();
+                    }
+                };
+
+                Func<EventPipeEventSource, Func<int>> _DoesTraceContainEvents = (source) => 
+                {
+                    int GCStartEvents = 0;
+                    int GCEndEvents = 0;
+                    source.Clr.GCStart += (eventData) => GCStartEvents += 1;
+                    source.Clr.GCStop += (eventData) => GCEndEvents += 1;
+
+                    int GCRestartEEStartEvents = 0;
+                    int GCRestartEEStopEvents = 0;           
+                    source.Clr.GCRestartEEStart += (eventData) => GCRestartEEStartEvents += 1;
+                    source.Clr.GCRestartEEStop += (eventData) => GCRestartEEStopEvents += 1; 
+
+                    int GCSuspendEEEvents = 0;
+                    int GCSuspendEEEndEvents = 0;
+                    source.Clr.GCSuspendEEStart += (eventData) => GCSuspendEEEvents += 1;
+                    source.Clr.GCSuspendEEStop += (eventData) => GCSuspendEEEndEvents += 1;
+
+                    int GCHeapStatsEvents =0;
+                    source.Clr.GCHeapStats += (eventData) => GCHeapStatsEvents +=1;
+
+                    return () => {
+                        Logger.logger.Log("Event counts validation");
+
+                        Logger.logger.Log("GCStartEvents: " + GCStartEvents);
+                        Logger.logger.Log("GCEndEvents: " + GCEndEvents);
+                        bool GCStartStopResult = GCStartEvents >= 50 && GCEndEvents >= 50 && Math.Abs(GCStartEvents - GCEndEvents) <=2;
+                        Logger.logger.Log("GCStartStopResult check: " + GCStartStopResult);
+
+                        Logger.logger.Log("GCRestartEEStartEvents: " + GCRestartEEStartEvents);
+                        Logger.logger.Log("GCRestartEEStopEvents: " + GCRestartEEStopEvents);
+                        bool GCRestartEEStartStopResult = GCRestartEEStartEvents >= 50 && GCRestartEEStopEvents >= 50;
+                        Logger.logger.Log("GCRestartEEStartStopResult check: " + GCRestartEEStartStopResult);
+
+                        Logger.logger.Log("GCSuspendEEEvents: " + GCSuspendEEEvents);
+                        Logger.logger.Log("GCSuspendEEEndEvents: " + GCSuspendEEEndEvents);
+                        bool GCSuspendEEStartStopResult = GCSuspendEEEvents >= 50 && GCSuspendEEEndEvents >= 50;
+                        Logger.logger.Log("GCSuspendEEStartStopResult check: " + GCSuspendEEStartStopResult);
+
+                        Logger.logger.Log("GCHeapStatsEvents: " + GCHeapStatsEvents);
+                        bool GCHeapStatsEventsResult = GCHeapStatsEvents >= 50 && GCHeapStatsEvents >= 50;
+                        Logger.logger.Log("GCHeapStatsEventsResult check: " + GCHeapStatsEventsResult);
+
+                        return GCStartStopResult && GCRestartEEStartStopResult && GCSuspendEEStartStopResult && GCHeapStatsEventsResult ? 100 : -1;
+                    };
+                };
+
+                var config = new SessionConfiguration(circularBufferSizeMB: (uint)Math.Pow(2, 10), format: EventPipeSerializationFormat.NetTrace,  providers: providers);
+
+                var ret = IpcTraceTest.RunAndValidateEventCounts(_expectedEventCounts, _eventGeneratingAction, config, _DoesTraceContainEvents);
+                Assert.Equal(100, ret);
+            }, output);
+        }
+
+        [Fact]
+        public async void GCWaitForPendingFinalizers_ProducesEvents()
+        {
+            await RemoteTestExecutorHelper.RunTestCaseAsync(() => 
+            {
+                Dictionary<string, ExpectedEventCount> _expectedEventCounts = new Dictionary<string, ExpectedEventCount>()
+                {
+                    { "Microsoft-Windows-DotNETRuntime", -1 },
+                    { "Microsoft-Windows-DotNETRuntimeRundown", -1 },
+                    { "Microsoft-DotNETCore-SampleProfiler", -1 }
+                };
+                
+                var providers = new List<Provider>()
+                {
+                    new Provider("Microsoft-DotNETCore-SampleProfiler"),
+                    //GCKeyword (0x1): 0b1
+                    new Provider("Microsoft-Windows-DotNETRuntime", 0b1, EventLevel.Informational)
+                };
+
+                Action _eventGeneratingAction = () => 
+                {
+                    for (int i = 0; i < 50; i++)
+                    {
+                        if (i % 10 == 0)
+                            Logger.logger.Log($"Called GC.Collect() {i} times...");
+                        TestClass testClass = new TestClass();
+                        testClass = null;
+                        GC.Collect();
+                        GC.WaitForPendingFinalizers();
+                    }
+                };
+
+                Func<EventPipeEventSource, Func<int>> _DoesTraceContainEvents = (source) => 
+                {
+                    int GCFinalizersEndEvents = 0;
+                    source.Clr.GCFinalizersStop += (eventData) => GCFinalizersEndEvents += 1;
+                    int GCFinalizersStartEvents = 0;
+                    source.Clr.GCFinalizersStart += (eventData) => GCFinalizersStartEvents += 1;
+                    return () => {
+                        Logger.logger.Log("Event counts validation");
+                        Logger.logger.Log("GCFinalizersEndEvents: " + GCFinalizersEndEvents);
+                        Logger.logger.Log("GCFinalizersStartEvents: " + GCFinalizersStartEvents);
+                        return GCFinalizersEndEvents >= 50 && GCFinalizersStartEvents >= 50 ? 100 : -1;
+                    };
+                };
+
+                var config = new SessionConfiguration(circularBufferSizeMB: (uint)Math.Pow(2, 10), format: EventPipeSerializationFormat.NetTrace,  providers: providers);
+
+                var ret = IpcTraceTest.RunAndValidateEventCounts(_expectedEventCounts, _eventGeneratingAction, config, _DoesTraceContainEvents);
+                Assert.Equal(100, ret);
+            }, output);
+        }
+
+        [Fact]
+        public async void GCCollect_ProducesVerboseEvents()
+        {
+            await RemoteTestExecutorHelper.RunTestCaseAsync(() => 
+            {
+                Dictionary<string, ExpectedEventCount> _expectedEventCounts = new Dictionary<string, ExpectedEventCount>()
+                {
+                    { "Microsoft-Windows-DotNETRuntime", -1 },
+                    { "Microsoft-Windows-DotNETRuntimeRundown", -1 },
+                    { "Microsoft-DotNETCore-SampleProfiler", -1 }
+                };
+                
+                var providers = new List<Provider>()
+                {
+                    new Provider("Microsoft-DotNETCore-SampleProfiler"),
+                    //GCKeyword (0x1): 0b1
+                    new Provider("Microsoft-Windows-DotNETRuntime", 0b1, EventLevel.Verbose)
+                };
+
+                Action _eventGeneratingAction = () => 
+                {
+                    List<string> testList = new List<string>();
+                    for(int i = 0; i < 100000000; i ++)
+                    {
+                        string t = "Test string!";
+                        testList.Add(t);
+                    }
+                    GC.Collect();
+                };
+
+                Func<EventPipeEventSource, Func<int>> _DoesTraceContainEvents = (source) => 
+                {
+                    int GCCreateSegmentEvents = 0;
+                    int GCFreeSegmentEvents = 0;
+                    source.Clr.GCCreateSegment += (eventData) => GCCreateSegmentEvents += 1;
+                    source.Clr.GCFreeSegment += (eventData) => GCFreeSegmentEvents += 1;
+
+                    int GCAllocationTickEvents = 0;
+                    source.Clr.GCAllocationTick += (eventData) => GCAllocationTickEvents += 1;
+
+                    int GCCreateConcurrentThreadEvents = 0;
+                    int GCTerminateConcurrentThreadEvents = 0;
+                    source.Clr.GCCreateConcurrentThread += (eventData) => GCCreateConcurrentThreadEvents += 1;
+                    source.Clr.GCTerminateConcurrentThread += (eventData) => GCTerminateConcurrentThreadEvents += 1;
+
+                    return () => {
+                        Logger.logger.Log("Event counts validation");
+
+                        Logger.logger.Log("GCCreateSegmentEvents: " + GCCreateSegmentEvents);
+                        Logger.logger.Log("GCFreeSegmentEvents: " + GCFreeSegmentEvents);
+                        bool GCSegmentResult = GCCreateSegmentEvents > 0 && GCFreeSegmentEvents > 0;
+                        Logger.logger.Log("GCSegmentResult: " + GCSegmentResult); 
+
+                        Logger.logger.Log("GCAllocationTickEvents: " + GCAllocationTickEvents);
+                        bool GCAllocationTickResult = GCAllocationTickEvents > 0;
+                        Logger.logger.Log("GCAllocationTickResult: " + GCAllocationTickResult); 
+
+                        Logger.logger.Log("GCCreateConcurrentThreadEvents: " + GCCreateConcurrentThreadEvents);
+                        //GCTerminateConcurrentThreadEvents not stable, ignore the verification
+                        Logger.logger.Log("GCTerminateConcurrentThreadEvents: " + GCTerminateConcurrentThreadEvents);
+                        bool GCConcurrentResult = GCCreateConcurrentThreadEvents > 0 && GCTerminateConcurrentThreadEvents >= 0;
+                        Logger.logger.Log("GCConcurrentResult: " + GCConcurrentResult);
+
+                        bool GCCollectResults = GCSegmentResult && GCAllocationTickResult && GCConcurrentResult;
+                        Logger.logger.Log("GCCollectResults: " + GCCollectResults);
+
+                        return GCCollectResults ? 100 : -1;
+                    };
+                };
+
+                var config = new SessionConfiguration(circularBufferSizeMB: (uint)Math.Pow(2, 10), format: EventPipeSerializationFormat.NetTrace,  providers: providers);
+
+                var ret = IpcTraceTest.RunAndValidateEventCounts(_expectedEventCounts, _eventGeneratingAction, config, _DoesTraceContainEvents);
+                Assert.Equal(100, ret);
+            }, output);
+        }
+    }
+}
\ No newline at end of file
diff --git a/src/tests/eventpipe/LoaderEvents.cs b/src/tests/eventpipe/LoaderEvents.cs
new file mode 100644 (file)
index 0000000..a654fdd
--- /dev/null
@@ -0,0 +1,117 @@
+// 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 Xunit;
+using System.IO;
+using System.Runtime.Loader;
+using System.Threading;
+using System.Reflection;
+using Xunit.Abstractions;
+using Microsoft.Diagnostics.Tools.RuntimeClient;
+using System.Collections.Generic;
+using System.Diagnostics.Tracing;
+using EventPipe.UnitTests.Common;
+using Microsoft.Diagnostics.Tracing;
+
+namespace EventPipe.UnitTests.LoaderEventsValidation
+{
+    public class AssemblyLoad : AssemblyLoadContext
+    {
+        public AssemblyLoad() : base(true)
+        {
+        }
+    }
+    public class ProviderTests
+    {
+        private readonly ITestOutputHelper output;
+
+        public ProviderTests(ITestOutputHelper outputHelper)
+        {
+            output = outputHelper;
+        }
+
+        [Fact]
+        public async void AssemblyLoad_ProducesEvents()
+        {
+            await RemoteTestExecutorHelper.RunTestCaseAsync(() => 
+            {
+                Dictionary<string, ExpectedEventCount> _expectedEventCounts = new Dictionary<string, ExpectedEventCount>()
+                {
+                    { "Microsoft-Windows-DotNETRuntime", -1 },
+                    { "Microsoft-Windows-DotNETRuntimeRundown", -1 }
+                };
+
+                var providers = new List<Provider>()
+                {
+                    //LoaderKeyword (0x8): 0b1000
+                    new Provider("Microsoft-Windows-DotNETRuntime", 0b1000, EventLevel.Informational)
+                };
+
+                string assemblyPath=null;
+                Action _eventGeneratingAction = () => 
+                {
+                    GetAssemblyPath();
+                    try
+                    {
+                        for(int i=0; i<100; i++)
+                        {
+                            if (i % 10 == 0)
+                                Logger.logger.Log($"Load/Unload Assembly {i} times...");
+                            AssemblyLoad assemblyLoad = new AssemblyLoad();
+                            assemblyLoad.LoadFromAssemblyPath(assemblyPath+"\\Microsoft.Diagnostics.Runtime.dll");
+                            assemblyLoad.Unload();
+                        }
+                    }
+                    catch(Exception ex)
+                    {
+                        Logger.logger.Log(ex.Message+ex.StackTrace);
+                    }
+                };
+
+                void GetAssemblyPath()
+                {
+                    assemblyPath = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
+                }
+
+
+                Func<EventPipeEventSource, Func<int>> _DoesTraceContainEvents = (source) => 
+                {
+                    int LoaderAssemblyLoadEvents = 0;
+                    int LoaderAssemblyUnloadEvents = 0;
+                    source.Clr.LoaderAssemblyLoad += (eventData) => LoaderAssemblyLoadEvents += 1;
+                    source.Clr.LoaderAssemblyUnload += (eventData) => LoaderAssemblyUnloadEvents += 1;
+
+                    int LoaderModuleLoadEvents = 0;
+                    int LoaderModuleUnloadEvents = 0;
+                    source.Clr.LoaderModuleLoad += (eventData) => LoaderModuleLoadEvents += 1;
+                    source.Clr.LoaderModuleUnload += (eventData) => LoaderModuleUnloadEvents += 1;
+
+                    return () => {
+                        Logger.logger.Log("Event counts validation");
+
+                        Logger.logger.Log("LoaderAssemblyLoadEvents: " + LoaderAssemblyLoadEvents);
+                        Logger.logger.Log("LoaderAssemblyUnloadEvents: " + LoaderAssemblyUnloadEvents);
+                        //Unload method just marks as unloadable, not unload immediately, so we check the unload events >=1 to make the tests stable
+                        bool LoaderAssemblyResult = LoaderAssemblyLoadEvents >= 100 && LoaderAssemblyUnloadEvents >= 1;
+                        Logger.logger.Log("LoaderAssemblyResult check: " + LoaderAssemblyResult);
+
+                        Logger.logger.Log("LoaderModuleLoadEvents: " + LoaderModuleLoadEvents);
+                        Logger.logger.Log("LoaderModuleUnloadEvents: " + LoaderModuleUnloadEvents);
+                        //Unload method just marks as unloadable, not unload immediately, so we check the unload events >=1 to make the tests stable
+                        bool LoaderModuleResult = LoaderModuleLoadEvents >= 100 && LoaderModuleUnloadEvents >= 1;
+                        Logger.logger.Log("LoaderModuleResult check: " + LoaderModuleResult);
+                        
+                        return LoaderAssemblyResult && LoaderModuleResult ? 100 : -1;
+                    };
+                };
+
+                var config = new SessionConfiguration(circularBufferSizeMB: (uint)Math.Pow(2, 10), format: EventPipeSerializationFormat.NetTrace,  providers: providers);
+
+                var ret = IpcTraceTest.RunAndValidateEventCounts(_expectedEventCounts, _eventGeneratingAction, config, _DoesTraceContainEvents);
+                Assert.Equal(100, ret);
+            }, output);
+        }
+    }
+}
\ No newline at end of file
diff --git a/src/tests/eventpipe/MethodEvents.cs b/src/tests/eventpipe/MethodEvents.cs
new file mode 100644 (file)
index 0000000..f1b5833
--- /dev/null
@@ -0,0 +1,103 @@
+// 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 Xunit;
+using System.ComponentModel;
+using Xunit.Abstractions;
+using Microsoft.Diagnostics.Tools.RuntimeClient;
+using System.Collections.Generic;
+using System.Diagnostics.Tracing;
+using EventPipe.UnitTests.Common;
+using Microsoft.Diagnostics.Tracing;
+
+namespace EventPipe.UnitTests.MethodEventsValidation
+{
+    public class M_verbose : IDisposable
+    {      
+        public bool IsZero(char c)
+        {
+            return c == 0;
+        }
+        public void Dispose()
+        {
+            GC.SuppressFinalize(this);
+        }
+    }
+    public class ProviderTests
+    {
+        private readonly ITestOutputHelper output;
+
+        public ProviderTests(ITestOutputHelper outputHelper)
+        {
+            output = outputHelper;
+        }
+
+        [Fact]
+        public async void MethodVerbose_ProducesEvents()
+        {
+            await RemoteTestExecutorHelper.RunTestCaseAsync(() => 
+            {
+                Dictionary<string, ExpectedEventCount> _expectedEventCounts = new Dictionary<string, ExpectedEventCount>()
+                {
+                    //registering Dynamic_All and Clr event callbacks will override each other, disable the check for the provider and check the events counts in the callback
+                    { "Microsoft-Windows-DotNETRuntimeRundown", -1 },
+                    { "Microsoft-DotNETCore-SampleProfiler", -1 }
+                };
+
+                var providers = new List<Provider>()
+                {
+                    new Provider("Microsoft-DotNETCore-SampleProfiler"),
+                    //MethodVerboseKeyword (0x10): 0b10000
+                    new Provider("Microsoft-Windows-DotNETRuntime", 0b10000, EventLevel.Verbose)
+                };
+                
+                Action _eventGeneratingAction = () => 
+                {
+                    for(int i=0; i<100; i++)
+                    {
+                        if (i % 10 == 0)
+                            Logger.logger.Log($"M_verbose occured {i} times...");
+
+                        using(M_verbose verbose = new M_verbose())
+                        {
+                            verbose.IsZero('f');
+                            verbose.Dispose();
+                        }
+                    }
+                };
+
+                Func<EventPipeEventSource, Func<int>> _DoesTraceContainEvents = (source) => 
+                {
+                    int MethodLoadVerboseEvents = 0;
+                    int MethodUnloadVerboseEvents = 0;
+                    source.Clr.MethodLoadVerbose += (eventData) => MethodLoadVerboseEvents += 1;
+                    source.Clr.MethodUnloadVerbose += (eventData) => MethodUnloadVerboseEvents += 1;
+
+                    int MethodJittingStartedEvents = 0;            
+                    source.Clr.MethodJittingStarted += (eventData) => MethodJittingStartedEvents += 1;
+
+                    return () => {
+                        Logger.logger.Log("Event counts validation");
+                        Logger.logger.Log("MethodLoadVerboseEvents: " + MethodLoadVerboseEvents);
+                        Logger.logger.Log("MethodUnloadVerboseEvents: " + MethodUnloadVerboseEvents);                        
+                        //MethodUnloadVerboseEvents not stable, ignore the verification
+                        bool MethodVerboseResult = MethodLoadVerboseEvents >= 1 && MethodUnloadVerboseEvents >= 0;
+                        Logger.logger.Log("MethodVerboseResult check: " + MethodVerboseResult);
+
+                        Logger.logger.Log("MethodJittingStartedEvents: " + MethodJittingStartedEvents);
+                        bool MethodJittingStartedResult = MethodJittingStartedEvents >= 1;
+                        Logger.logger.Log("MethodJittingStartedResult check: " + MethodJittingStartedResult);
+                        return MethodVerboseResult && MethodJittingStartedResult ? 100 : -1;
+                    };
+                };
+
+                var config = new SessionConfiguration(circularBufferSizeMB: (uint)Math.Pow(2, 10), format: EventPipeSerializationFormat.NetTrace,  providers: providers);
+
+                var ret = IpcTraceTest.RunAndValidateEventCounts(_expectedEventCounts, _eventGeneratingAction, config, _DoesTraceContainEvents);
+                Assert.Equal(100, ret);
+            }, output);
+        }
+    }
+}
\ No newline at end of file
diff --git a/src/tests/eventpipe/ThreadPoolEvents.cs b/src/tests/eventpipe/ThreadPoolEvents.cs
new file mode 100644 (file)
index 0000000..94cc391
--- /dev/null
@@ -0,0 +1,95 @@
+// 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 Xunit;
+using System.IO;
+using System.Threading;
+using System.Threading.Tasks;
+using Xunit.Abstractions;
+using Microsoft.Diagnostics.Tools.RuntimeClient;
+using System.Collections.Generic;
+using System.Diagnostics.Tracing;
+using EventPipe.UnitTests.Common;
+using Microsoft.Diagnostics.Tracing;
+
+namespace EventPipe.UnitTests.ThreadPoolValidation
+{
+    public class ProviderTests
+    {
+        private readonly ITestOutputHelper output;
+
+        public ProviderTests(ITestOutputHelper outputHelper)
+        {
+            output = outputHelper;
+        }
+
+        [Fact]
+        public async void ThreadPool_ProducesEvents()
+        {
+            await RemoteTestExecutorHelper.RunTestCaseAsync(() => 
+            {
+                Dictionary<string, ExpectedEventCount> _expectedEventCounts = new Dictionary<string, ExpectedEventCount>()
+                {
+                    { "Microsoft-Windows-DotNETRuntime", -1 },
+                    { "Microsoft-Windows-DotNETRuntimeRundown", -1 }
+                };
+
+                var providers = new List<Provider>()
+                {
+                    //ThreadingKeyword (0x10000): 0b10000_0000_0000_0000
+                    new Provider("Microsoft-Windows-DotNETRuntime", 0b10000_0000_0000_0000, EventLevel.Informational)
+                };
+
+                Action _eventGeneratingAction = () => 
+                {
+                    Task[] taskArray = new Task[1000];
+                    for (int i = 0; i < 1000; i++)
+                    {
+                        if (i % 10 == 0)
+                            Logger.logger.Log($"Create new task {i} times...");
+                        taskArray[i] = Task.Run(() => TestTask());
+                    }
+                    Task.WaitAll(taskArray);
+                };
+
+                void TestTask()
+                {
+                    Thread.Sleep(100);
+                }
+
+                Func<EventPipeEventSource, Func<int>> _DoesTraceContainEvents = (source) => 
+                {
+                    int ThreadStartEvents = 0;
+                    source.Clr.ThreadPoolWorkerThreadStart += (eventData) => ThreadStartEvents += 1;
+
+                    int ThreadPoolWorkerThreadAdjustmentSampleEvents = 0;
+                    int ThreadPoolWorkerThreadAdjustmentAdjustmentEvents = 0;
+                    source.Clr.ThreadPoolWorkerThreadAdjustmentSample += (eventData) => ThreadPoolWorkerThreadAdjustmentSampleEvents += 1;
+                    source.Clr.ThreadPoolWorkerThreadAdjustmentAdjustment += (eventData) => ThreadPoolWorkerThreadAdjustmentAdjustmentEvents += 1;
+
+                    return () => {
+                        Logger.logger.Log("Event counts validation");
+
+                        Logger.logger.Log("ThreadStartEvents: " + ThreadStartEvents);
+                        bool ThreadStartStopResult = ThreadStartEvents >= 1;
+                        Logger.logger.Log("ThreadStartStopResult check: " + ThreadStartStopResult);
+
+                        Logger.logger.Log("ThreadPoolWorkerThreadAdjustmentSampleEvents: " + ThreadPoolWorkerThreadAdjustmentSampleEvents);
+                        Logger.logger.Log("ThreadPoolWorkerThreadAdjustmentAdjustmentEvents: " + ThreadPoolWorkerThreadAdjustmentAdjustmentEvents);
+                        bool ThreadAdjustmentResult = ThreadPoolWorkerThreadAdjustmentSampleEvents >= 1 && ThreadPoolWorkerThreadAdjustmentAdjustmentEvents >= 1;
+                        Logger.logger.Log("ThreadAdjustmentResult check: " + ThreadAdjustmentResult);
+                        
+                        return ThreadStartStopResult && ThreadAdjustmentResult ? 100 : -1;
+                    };
+                };
+
+                var config = new SessionConfiguration(circularBufferSizeMB: (uint)Math.Pow(2, 10), format: EventPipeSerializationFormat.NetTrace,  providers: providers);
+
+                var ret = IpcTraceTest.RunAndValidateEventCounts(_expectedEventCounts, _eventGeneratingAction, config, _DoesTraceContainEvents);
+                Assert.Equal(100, ret);
+            }, output);
+        }
+    }
+}
\ No newline at end of file