Handle Counter Polling Interval of 0 (#53836)
authorJohn Salem <josalem@microsoft.com>
Thu, 10 Jun 2021 22:21:30 +0000 (15:21 -0700)
committerGitHub <noreply@github.com>
Thu, 10 Jun 2021 22:21:30 +0000 (15:21 -0700)
src/libraries/System.Private.CoreLib/src/System/Diagnostics/Tracing/CounterGroup.cs
src/tests/tracing/eventcounter/gh53564.cs [new file with mode: 0644]
src/tests/tracing/eventcounter/gh53564.csproj [new file with mode: 0644]

index 5d85b20..d60be1b 100644 (file)
@@ -251,10 +251,9 @@ namespace System.Diagnostics.Tracing
                 lock (s_counterGroupLock)
                 {
                     _timeStampSinceCollectionStarted = now;
-                    do
-                    {
-                        _nextPollingTimeStamp += new TimeSpan(0, 0, 0, 0, _pollingIntervalInMilliseconds);
-                    } while (_nextPollingTimeStamp <= now);
+                    TimeSpan delta = now - _nextPollingTimeStamp;
+                    if (delta > TimeSpan.Zero && _pollingIntervalInMilliseconds > 0)
+                        _nextPollingTimeStamp += TimeSpan.FromMilliseconds(_pollingIntervalInMilliseconds * Math.Ceiling(delta.TotalMilliseconds / _pollingIntervalInMilliseconds));
                 }
             }
         }
diff --git a/src/tests/tracing/eventcounter/gh53564.cs b/src/tests/tracing/eventcounter/gh53564.cs
new file mode 100644 (file)
index 0000000..148c485
--- /dev/null
@@ -0,0 +1,99 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+#if USE_MDT_EVENTSOURCE
+using Microsoft.Diagnostics.Tracing;
+#else
+using System.Diagnostics.Tracing;
+#endif
+using System;
+using System.Collections.Generic;
+using System.Threading;
+using System.Threading.Tasks;
+using System.Diagnostics;
+
+namespace gh53564Tests
+{
+    public class RuntimeCounterListener : EventListener
+    {
+        public RuntimeCounterListener(){}
+
+        private DateTime? setToZeroTimestamp = null;
+        private DateTime? mostRecentTimestamp = null;
+        public ManualResetEvent ReadyToVerify { get; } = new ManualResetEvent(initialState: false);
+
+        protected override void OnEventSourceCreated(EventSource source)
+        {
+            if (source.Name.Equals("System.Runtime"))
+            {
+                Dictionary<string, string> refreshInterval = new Dictionary<string, string>();
+
+                Console.WriteLine($"[{DateTime.Now:hh:mm:ss.fff}] Setting interval to 1");
+                // first set interval to 1 seconds
+                refreshInterval["EventCounterIntervalSec"] = "1";
+                EnableEvents(source, EventLevel.Informational, (EventKeywords)(-1), refreshInterval);
+
+                // wait a moment to get some events
+                Thread.Sleep(TimeSpan.FromSeconds(3));
+
+                // then set interval to 0
+                Console.WriteLine($"[{DateTime.Now:hh:mm:ss.fff}] Setting interval to 0");
+                refreshInterval["EventCounterIntervalSec"] = "0";
+                EnableEvents(source, EventLevel.Informational, (EventKeywords)(-1), refreshInterval);
+                setToZeroTimestamp = DateTime.Now + TimeSpan.FromSeconds(1); // Stash timestamp 1 second after setting to 0
+
+                // then attempt to set interval back to 1
+                Thread.Sleep(TimeSpan.FromSeconds(3));
+                Console.WriteLine($"[{DateTime.Now:hh:mm:ss.fff}] Setting interval to 1");
+                refreshInterval["EventCounterIntervalSec"] = "1";
+                EnableEvents(source, EventLevel.Informational, (EventKeywords)(-1), refreshInterval);
+                Thread.Sleep(TimeSpan.FromSeconds(3));
+                Console.WriteLine($"[{DateTime.Now:hh:mm:ss.fff}] Setting ReadyToVerify");
+                ReadyToVerify.Set();
+            }
+        }
+
+        protected override void OnEventWritten(EventWrittenEventArgs eventData)
+        {
+            mostRecentTimestamp = eventData.TimeStamp;
+        }
+
+        public bool Verify()
+        {
+            if (!ReadyToVerify.WaitOne(0))
+                return false;
+
+            return (setToZeroTimestamp is null || mostRecentTimestamp is null) ? false : setToZeroTimestamp < mostRecentTimestamp;
+        }
+    }
+
+    public partial class TestRuntimeEventCounter
+    {
+        public static int Main(string[] args)
+        {
+            // Create an EventListener.
+            using (RuntimeCounterListener myListener = new RuntimeCounterListener())
+            {
+                if (myListener.ReadyToVerify.WaitOne(TimeSpan.FromSeconds(15)))
+                {
+                Console.WriteLine($"[{DateTime.Now:hh:mm:ss.fff}] Ready to verify");
+                    if (myListener.Verify())
+                    {
+                        Console.WriteLine("Test passed");
+                        return 100;
+                    }
+                    else
+                    {
+                        Console.WriteLine($"Test Failed - did not see one or more of the expected runtime counters.");
+                        return 1;
+                    }
+                }
+                else
+                {
+                    Console.WriteLine("Test Failed - timed out waiting for reset");
+                    return 1;
+                }
+            }
+        }
+    }
+}
diff --git a/src/tests/tracing/eventcounter/gh53564.csproj b/src/tests/tracing/eventcounter/gh53564.csproj
new file mode 100644 (file)
index 0000000..5c2b810
--- /dev/null
@@ -0,0 +1,17 @@
+<Project Sdk="Microsoft.NET.Sdk">
+  <PropertyGroup>
+    <OutputType>Exe</OutputType>
+    <CLRTestKind>BuildAndRun</CLRTestKind>
+    <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
+    <CLRTestPriority>0</CLRTestPriority>
+    <GCStressIncompatible>true</GCStressIncompatible>
+    <!-- This test is timing sensitive and JIT timing affects the results of the test -->
+    <JitOptimizationSensitive>true</JitOptimizationSensitive>
+    <!-- This test has a secondary thread with an infinite loop -->
+    <UnloadabilityIncompatible>true</UnloadabilityIncompatible>
+  </PropertyGroup>
+  <ItemGroup>
+    <Compile Include="gh53564.cs" />
+    <ProjectReference Include="../common/common.csproj" />
+  </ItemGroup>
+</Project>