Skip to content

Commit

Permalink
Fix #53564 * default internal state to DateTime.MaxValue * re-check I…
Browse files Browse the repository at this point in the history
…sEnabled after not holding lock * add test for setting interval to 0
  • Loading branch information
John Salem authored and github-actions committed Jun 10, 2021
1 parent a33b642 commit 8521556
Show file tree
Hide file tree
Showing 3 changed files with 126 additions and 5 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -116,14 +116,15 @@ internal static CounterGroup GetCounterGroup(EventSource eventSource)

private DateTime _timeStampSinceCollectionStarted;
private int _pollingIntervalInMilliseconds;
private DateTime _nextPollingTimeStamp;
private DateTime _nextPollingTimeStamp = DateTime.MaxValue;

private void EnableTimer(float pollingIntervalInSeconds)
{
Debug.Assert(Monitor.IsEntered(s_counterGroupLock));
if (pollingIntervalInSeconds <= 0)
{
_pollingIntervalInMilliseconds = 0;
_nextPollingTimeStamp = DateTime.MaxValue;
}
else if (_pollingIntervalInMilliseconds == 0 || pollingIntervalInSeconds * 1000 < _pollingIntervalInMilliseconds)
{
Expand Down Expand Up @@ -173,6 +174,7 @@ private void EnableTimer(float pollingIntervalInSeconds)
private void DisableTimer()
{
_pollingIntervalInMilliseconds = 0;
_nextPollingTimeStamp = DateTime.MaxValue;
s_counterGroupEnabledList?.Remove(this);
}

Expand Down Expand Up @@ -235,11 +237,14 @@ private void OnTimer()

lock (s_counterGroupLock)
{
_timeStampSinceCollectionStarted = now;
do
if (_eventSource.IsEnabled())
{
_nextPollingTimeStamp += new TimeSpan(0, 0, 0, 0, _pollingIntervalInMilliseconds);
} while (_nextPollingTimeStamp <= now);
_timeStampSinceCollectionStarted = now;
do
{
_nextPollingTimeStamp += new TimeSpan(0, 0, 0, 0, _pollingIntervalInMilliseconds);
} while (_nextPollingTimeStamp <= now);
}
}
}
}
Expand Down
99 changes: 99 additions & 0 deletions src/tests/tracing/eventcounter/gh53564.cs
Original file line number Diff line number Diff line change
@@ -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;
}
}
}
}
}
17 changes: 17 additions & 0 deletions src/tests/tracing/eventcounter/gh53564.csproj
Original file line number Diff line number Diff line change
@@ -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>

0 comments on commit 8521556

Please sign in to comment.