diff --git a/src/libraries/System.Private.CoreLib/src/System/Diagnostics/Tracing/CounterGroup.cs b/src/libraries/System.Private.CoreLib/src/System/Diagnostics/Tracing/CounterGroup.cs index d08c6949616c10..8bdda96a727971 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Diagnostics/Tracing/CounterGroup.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Diagnostics/Tracing/CounterGroup.cs @@ -2,6 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. using System.Collections.Generic; +using System.Diagnostics; using System.Runtime.Versioning; using System.Threading; @@ -92,7 +93,7 @@ private void OnEventSourceCommand(object? sender, EventCommandEventArgs e) Debug.Assert((s_counterGroupEnabledList == null && !_eventSource.IsEnabled()) || (_eventSource.IsEnabled() && s_counterGroupEnabledList!.Contains(this)) - || (_pollingIntervalInMilliseconds == 0 && !s_counterGroupEnabledList!.Contains(this)) + || (_pollingInterval == TimeSpan.Zero && !s_counterGroupEnabledList!.Contains(this)) || (!_eventSource.IsEnabled() && !s_counterGroupEnabledList!.Contains(this))); } } @@ -142,22 +143,25 @@ internal static CounterGroup GetCounterGroup(EventSource eventSource) #region Timer Processing - private DateTime _timeStampSinceCollectionStarted; - private int _pollingIntervalInMilliseconds; - private DateTime _nextPollingTimeStamp; + private long _baseTimestamp; + private TimeSpan _timeSinceCollectionStarted; + private TimeSpan _pollingInterval; + private TimeSpan _nextPollingOffset; private void EnableTimer(float pollingIntervalInSeconds) { Debug.Assert(pollingIntervalInSeconds > 0); Debug.Assert(Monitor.IsEntered(s_counterGroupLock)); - if (_pollingIntervalInMilliseconds == 0 || pollingIntervalInSeconds * 1000 < _pollingIntervalInMilliseconds) + TimeSpan interval = TimeSpan.FromSeconds(pollingIntervalInSeconds); + if (_pollingInterval == TimeSpan.Zero || interval < _pollingInterval) { - _pollingIntervalInMilliseconds = (int)(pollingIntervalInSeconds * 1000); + _pollingInterval = interval; // Schedule IncrementingPollingCounter reset and synchronously reset other counters HandleCountersReset(); - _timeStampSinceCollectionStarted = DateTime.UtcNow; - _nextPollingTimeStamp = DateTime.UtcNow + new TimeSpan(0, 0, (int)pollingIntervalInSeconds); + _baseTimestamp = Stopwatch.GetTimestamp(); + _timeSinceCollectionStarted = TimeSpan.Zero; + _nextPollingOffset = _pollingInterval; // Create the polling thread and init all the shared state if needed if (s_pollingThread == null) @@ -186,7 +190,7 @@ private void EnableTimer(float pollingIntervalInSeconds) private void DisableTimer() { Debug.Assert(Monitor.IsEntered(s_counterGroupLock)); - _pollingIntervalInMilliseconds = 0; + _pollingInterval = TimeSpan.Zero; s_counterGroupEnabledList?.Remove(this); if (s_needsResetIncrementingPollingCounters.Count > 0) @@ -225,15 +229,15 @@ private void OnTimer() { if (_eventSource.IsEnabled()) { - DateTime now; + TimeSpan nowOffset; TimeSpan elapsed; - int pollingIntervalInMilliseconds; + TimeSpan pollingInterval; DiagnosticCounter[] counters; lock (s_counterGroupLock) { - now = DateTime.UtcNow; - elapsed = now - _timeStampSinceCollectionStarted; - pollingIntervalInMilliseconds = _pollingIntervalInMilliseconds; + nowOffset = Stopwatch.GetElapsedTime(_baseTimestamp); + elapsed = nowOffset - _timeSinceCollectionStarted; + pollingInterval = _pollingInterval; counters = new DiagnosticCounter[_counters.Count]; _counters.CopyTo(counters); } @@ -253,16 +257,23 @@ private void OnTimer() // written to the old session or the new session. The behavior change is not being treated as a // significant problem to address for now, but we can come back and address it if it turns out to // be an actual issue. - counter.WritePayload((float)elapsed.TotalSeconds, pollingIntervalInMilliseconds); + counter.WritePayload((float)elapsed.TotalSeconds, (int)pollingInterval.TotalMilliseconds); } lock (s_counterGroupLock) { - _timeStampSinceCollectionStarted = now; - TimeSpan delta = now - _nextPollingTimeStamp; - delta = _pollingIntervalInMilliseconds > delta.TotalMilliseconds ? TimeSpan.FromMilliseconds(_pollingIntervalInMilliseconds) : delta; - if (_pollingIntervalInMilliseconds > 0) - _nextPollingTimeStamp += TimeSpan.FromMilliseconds(_pollingIntervalInMilliseconds * Math.Ceiling(delta.TotalMilliseconds / _pollingIntervalInMilliseconds)); + _timeSinceCollectionStarted = nowOffset; + TimeSpan delta = nowOffset - _nextPollingOffset; + if (delta < _pollingInterval) + { + delta = _pollingInterval; + } + + if (_pollingInterval > TimeSpan.Zero) + { + long missed = (delta.Ticks + _pollingInterval.Ticks - 1) / _pollingInterval.Ticks; + _nextPollingOffset += new TimeSpan(missed * _pollingInterval.Ticks); + } } } } @@ -292,14 +303,14 @@ private static void PollForValues() sleepEvent = s_pollingThreadSleepEvent; foreach (CounterGroup counterGroup in s_counterGroupEnabledList!) { - DateTime now = DateTime.UtcNow; - if (counterGroup._nextPollingTimeStamp < now + new TimeSpan(0, 0, 0, 0, 1)) + TimeSpan nowOffset = Stopwatch.GetElapsedTime(counterGroup._baseTimestamp); + TimeSpan timeUntilNextPoll = counterGroup._nextPollingOffset - nowOffset; + if (timeUntilNextPoll < TimeSpan.FromMilliseconds(1)) { onTimers.Add(counterGroup); } - int millisecondsTillNextPoll = (int)((counterGroup._nextPollingTimeStamp - now).TotalMilliseconds); - millisecondsTillNextPoll = Math.Max(1, millisecondsTillNextPoll); + int millisecondsTillNextPoll = Math.Max(1, (int)timeUntilNextPoll.TotalMilliseconds); sleepDurationInMilliseconds = Math.Min(sleepDurationInMilliseconds, millisecondsTillNextPoll); }