Skip to content

Commit

Permalink
Add GC committed bytes counter (dotnet#50604)
Browse files Browse the repository at this point in the history
* Add GC committed bytes counter

* Add tests

* fix test
  • Loading branch information
sywhang authored Apr 3, 2021
1 parent 872a4ce commit e52322d
Show file tree
Hide file tree
Showing 3 changed files with 135 additions and 0 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ internal sealed partial class RuntimeEventSource : EventSource
private IncrementingPollingCounter? _allocRateCounter;
private PollingCounter? _timerCounter;
private PollingCounter? _fragmentationCounter;
private PollingCounter? _committedCounter;

#if !MONO
private IncrementingPollingCounter? _exceptionCounter;
Expand Down Expand Up @@ -76,6 +77,7 @@ protected override void OnEventCommand(EventCommandEventArgs command)
var gcInfo = GC.GetGCMemoryInfo();
return gcInfo.HeapSizeBytes != 0 ? gcInfo.FragmentedBytes * 100d / gcInfo.HeapSizeBytes : 0;
}) { DisplayName = "GC Fragmentation", DisplayUnits = "%" };
_committedCounter ??= new PollingCounter("gc-committed", this, () => GC.GetGCMemoryInfo().TotalCommittedBytes) { DisplayName = "GC Committed Bytes", DisplayUnits = "B" };
#if !MONO
_exceptionCounter ??= new IncrementingPollingCounter("exception-count", this, () => Exception.GetExceptionCount()) { DisplayName = "Exception Count", DisplayRateTimeScale = new TimeSpan(0, 0, 1) };
_gcTimeCounter ??= new PollingCounter("time-in-gc", this, () => GC.GetLastGCPercentTimeInGC()) { DisplayName = "% Time in GC since last GC", DisplayUnits = "%" };
Expand Down
116 changes: 116 additions & 0 deletions src/tests/tracing/eventcounter/runtimecounters.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
// 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 RuntimeEventCounterTests
{
public class RuntimeCounterListener : EventListener
{
public RuntimeCounterListener()
{
observedRuntimeCounters = new Dictionary<string, bool>() {
{ "cpu-usage" , false },
{ "working-set", false },
{ "gc-heap-size", false },
{ "gen-0-gc-count", false },
{ "gen-1-gc-count", false },
{ "gen-2-gc-count", false },
{ "threadpool-thread-count", false },
{ "monitor-lock-contention-count", false },
{ "threadpool-queue-length", false },
{ "threadpool-completed-items-count", false },
{ "alloc-rate", false },
{ "active-timer-count", false },
{ "gc-fragmentation", false },
{ "gc-committed", false },
{ "exception-count", false },
{ "time-in-gc", false },
{ "gen-0-size", false },
{ "gen-1-size", false },
{ "gen-2-size", false },
{ "loh-size", false },
{ "poh-size", false },
{ "assembly-count", false },
{ "il-bytes-jitted", false },
{ "methods-jitted-count", false }
};
}
private Dictionary<string, bool> observedRuntimeCounters;

protected override void OnEventSourceCreated(EventSource source)
{
if (source.Name.Equals("System.Runtime"))
{
Dictionary<string, string> refreshInterval = new Dictionary<string, string>();
refreshInterval.Add("EventCounterIntervalSec", "1");
EnableEvents(source, EventLevel.Informational, (EventKeywords)(-1), refreshInterval);
}
}

protected override void OnEventWritten(EventWrittenEventArgs eventData)
{

for (int i = 0; i < eventData.Payload.Count; i++)
{
IDictionary<string, object> eventPayload = eventData.Payload[i] as IDictionary<string, object>;
if (eventPayload != null)
{
foreach (KeyValuePair<string, object> payload in eventPayload)
{
if (payload.Key.Equals("Name"))
observedRuntimeCounters[payload.Value.ToString()] = true;
}
}
}
}

public bool Verify()
{
foreach (string counterName in observedRuntimeCounters.Keys)
{
if (!observedRuntimeCounters[counterName])
{
Console.WriteLine($"Did not see {counterName}");
return false;
}
else
{
Console.WriteLine($"Saw {counterName}");
}
}
return true;
}
}

public partial class TestRuntimeEventCounter
{
public static int Main(string[] args)
{
// Create an EventListener.
using (RuntimeCounterListener myListener = new RuntimeCounterListener())
{
Thread.Sleep(3000);
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;
}
}
}
}
}
17 changes: 17 additions & 0 deletions src/tests/tracing/eventcounter/runtimecounters.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="runtimecounters.cs" />
<ProjectReference Include="../common/common.csproj" />
</ItemGroup>
</Project>

0 comments on commit e52322d

Please sign in to comment.