Skip to content

Commit fbf7306

Browse files
github-actions[bot]John Salem
and
John Salem
authored
[release/5.0] Handle Counter Polling Interval of 0 (#54035)
* Fix #53564 * default internal state to DateTime.MaxValue * re-check IsEnabled after not holding lock * add test for setting interval to 0 * simplify fix to just a for loop * remove one more line * Remove loop * Update test to match #54081 Co-authored-by: John Salem <[email protected]>
1 parent ee1ce16 commit fbf7306

File tree

3 files changed

+129
-4
lines changed

3 files changed

+129
-4
lines changed

src/libraries/System.Private.CoreLib/src/System/Diagnostics/Tracing/CounterGroup.cs

+3-4
Original file line numberDiff line numberDiff line change
@@ -236,10 +236,9 @@ private void OnTimer()
236236
lock (s_counterGroupLock)
237237
{
238238
_timeStampSinceCollectionStarted = now;
239-
do
240-
{
241-
_nextPollingTimeStamp += new TimeSpan(0, 0, 0, 0, _pollingIntervalInMilliseconds);
242-
} while (_nextPollingTimeStamp <= now);
239+
TimeSpan delta = now - _nextPollingTimeStamp;
240+
if (delta > TimeSpan.Zero && _pollingIntervalInMilliseconds > 0)
241+
_nextPollingTimeStamp += TimeSpan.FromMilliseconds(_pollingIntervalInMilliseconds * Math.Ceiling(delta.TotalMilliseconds / _pollingIntervalInMilliseconds));
243242
}
244243
}
245244
}
+109
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
// Licensed to the .NET Foundation under one or more agreements.
2+
// The .NET Foundation licenses this file to you under the MIT license.
3+
4+
#if USE_MDT_EVENTSOURCE
5+
using Microsoft.Diagnostics.Tracing;
6+
#else
7+
using System.Diagnostics.Tracing;
8+
#endif
9+
using System;
10+
using System.Collections.Generic;
11+
using System.Threading;
12+
using System.Threading.Tasks;
13+
using System.Diagnostics;
14+
15+
namespace gh53564Tests
16+
{
17+
public class RuntimeCounterListener : EventListener
18+
{
19+
public RuntimeCounterListener(){}
20+
21+
private DateTime? setToZeroTimestamp = null;
22+
private DateTime? mostRecentTimestamp = null;
23+
private ManualResetEvent setToZero = new ManualResetEvent(initialState: false);
24+
public ManualResetEvent ReadyToVerify { get; } = new ManualResetEvent(initialState: false);
25+
26+
protected override void OnEventSourceCreated(EventSource source)
27+
{
28+
if (source.Name.Equals("System.Runtime"))
29+
{
30+
Dictionary<string, string> refreshInterval = new Dictionary<string, string>();
31+
32+
Console.WriteLine($"[{DateTime.UtcNow:hh:mm:ss.fff}] OnEventSourceCreated :: Setting interval to 1");
33+
// first set interval to 1 seconds
34+
refreshInterval["EventCounterIntervalSec"] = "1";
35+
EnableEvents(source, EventLevel.Informational, (EventKeywords)(-1), refreshInterval);
36+
37+
// wait a moment to get some events
38+
Thread.Sleep(TimeSpan.FromSeconds(3));
39+
40+
// then set interval to 0
41+
Console.WriteLine($"[{DateTime.UtcNow:hh:mm:ss.fff}] OnEventSourceCreated :: Setting interval to 0");
42+
refreshInterval["EventCounterIntervalSec"] = "0";
43+
EnableEvents(source, EventLevel.Informational, (EventKeywords)(-1), refreshInterval);
44+
setToZeroTimestamp = DateTime.UtcNow + TimeSpan.FromSeconds(1); // Stash timestamp 1 second after setting to 0
45+
setToZero.Set();
46+
47+
// then attempt to set interval back to 1
48+
Thread.Sleep(TimeSpan.FromSeconds(3));
49+
Console.WriteLine($"[{DateTime.UtcNow:hh:mm:ss.fff}] OnEventSourceCreated :: Setting interval to 1");
50+
refreshInterval["EventCounterIntervalSec"] = "1";
51+
EnableEvents(source, EventLevel.Informational, (EventKeywords)(-1), refreshInterval);
52+
}
53+
}
54+
55+
protected override void OnEventWritten(EventWrittenEventArgs eventData)
56+
{
57+
if (!ReadyToVerify.WaitOne(0))
58+
{
59+
mostRecentTimestamp = eventData.TimeStamp;
60+
if (setToZero.WaitOne(0) && mostRecentTimestamp > setToZeroTimestamp)
61+
{
62+
Console.WriteLine($"[{DateTime.UtcNow:hh:mm:ss.fff}] OnEventWritten :: Setting ReadyToVerify");
63+
ReadyToVerify.Set();
64+
}
65+
}
66+
}
67+
68+
public bool Verify()
69+
{
70+
if (!ReadyToVerify.WaitOne(0))
71+
return false;
72+
73+
Console.WriteLine($"[{DateTime.UtcNow:hh:mm:ss.fff}] Verify :: Verifying");
74+
Console.WriteLine($"[{DateTime.UtcNow:hh:mm:ss.fff}] setToZeroTimestamp = {setToZeroTimestamp?.ToString("hh:mm:ss.fff") ?? "NULL"}");
75+
Console.WriteLine($"[{DateTime.UtcNow:hh:mm:ss.fff}] mostRecentTimestamp = {mostRecentTimestamp?.ToString("hh:mm:ss.fff") ?? "NULL"}");
76+
77+
return (setToZeroTimestamp is null || mostRecentTimestamp is null) ? false : setToZeroTimestamp < mostRecentTimestamp;
78+
}
79+
}
80+
81+
public partial class TestRuntimeEventCounter
82+
{
83+
public static int Main(string[] args)
84+
{
85+
// Create an EventListener.
86+
using (RuntimeCounterListener myListener = new RuntimeCounterListener())
87+
{
88+
if (myListener.ReadyToVerify.WaitOne(TimeSpan.FromSeconds(30)))
89+
{
90+
if (myListener.Verify())
91+
{
92+
Console.WriteLine("Test passed");
93+
return 100;
94+
}
95+
else
96+
{
97+
Console.WriteLine($"Test Failed - did not see one or more of the expected runtime counters.");
98+
return 1;
99+
}
100+
}
101+
else
102+
{
103+
Console.WriteLine("Test Failed - timed out waiting for reset");
104+
return 1;
105+
}
106+
}
107+
}
108+
}
109+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
<Project Sdk="Microsoft.NET.Sdk">
2+
<PropertyGroup>
3+
<OutputType>Exe</OutputType>
4+
<CLRTestKind>BuildAndRun</CLRTestKind>
5+
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
6+
<CLRTestPriority>0</CLRTestPriority>
7+
<GCStressIncompatible>true</GCStressIncompatible>
8+
<!-- This test is timing sensitive and JIT timing affects the results of the test -->
9+
<JitOptimizationSensitive>true</JitOptimizationSensitive>
10+
<!-- This test has a secondary thread with an infinite loop -->
11+
<UnloadabilityIncompatible>true</UnloadabilityIncompatible>
12+
</PropertyGroup>
13+
<ItemGroup>
14+
<Compile Include="gh53564.cs" />
15+
<ProjectReference Include="../common/common.csproj" />
16+
</ItemGroup>
17+
</Project>

0 commit comments

Comments
 (0)