Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add more runtime GC counters #38851

Merged
merged 13 commits into from
Aug 4, 2020
6 changes: 6 additions & 0 deletions src/coreclr/src/System.Private.CoreLib/src/System/GC.cs
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,12 @@ internal enum GC_ALLOC_FLAGS
[MethodImpl(MethodImplOptions.InternalCall)]
internal static extern ulong GetGenerationSize(int gen);

[MethodImpl(MethodImplOptions.InternalCall)]
sywhang marked this conversation as resolved.
Show resolved Hide resolved
internal static extern ulong GetGenerationTimeBetweenGC(int gen);

[MethodImpl(MethodImplOptions.InternalCall)]
internal static extern int GetGenerationLastGCDuration(int gen);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If this information is important for telemetry, should we consider putting it on GCMemoryInfo?
cc: @maoni

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

GCMemoryInfo already includes pause info. I don't think we want to include duration because for BGC it's not useful; for non BGC it's basically the same as pause.

the user of the GetGCMemoryInfo API would detect the GC frequency to the precision their sampling interval allows, eg if they call this API once every second and detected 2 gen0 GCs, they don't know how long exactly elapsed between the 2 GCs but they know they have 2 gen0 GCs in 1s.


[DllImport(RuntimeHelpers.QCall, CharSet = CharSet.Unicode)]
private static extern void _AddMemoryPressure(ulong bytesAllocated);

Expand Down
31 changes: 30 additions & 1 deletion src/coreclr/src/gc/gcee.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,14 @@ uint64_t g_TotalTimeInGC = 0;
uint64_t g_TotalTimeSinceLastGCEnd = 0;

uint32_t g_percentTimeInGCSinceLastGC = 0;
uint64_t g_TimeBetweenLastTwoGCs = 0;

size_t g_GenerationSizes[total_generation_count];
size_t g_GenerationPromotedSizes[total_generation_count];
uint64_t g_GenerationTimeBetweenGCs[total_generation_count];
sywhang marked this conversation as resolved.
Show resolved Hide resolved
uint64_t g_GenerationLastGCTimestamp[total_generation_count];
uint32_t g_GenerationLastGCDuration[total_generation_count];


void GCHeap::UpdatePreGCCounters()
{
Expand Down Expand Up @@ -181,9 +186,15 @@ void GCHeap::UpdatePostGCCounters()

#endif // FEATURE_EVENT_TRACE

// Compute Time in GC
uint64_t _currentPerfCounterTimer = GCToOSInterface::QueryPerformanceCounter();
uint64_t _currentPerfCounterFreq = (uint64_t)GCToOSInterface::QueryPerformanceFrequency();

// Compute Time between last GCs in ms
g_GenerationTimeBetweenGCs[condemned_gen] = (_currentPerfCounterTimer - g_GenerationLastGCTimestamp[condemned_gen]) * 1000 / _currentPerfCounterFreq;
g_GenerationLastGCTimestamp[condemned_gen] = _currentPerfCounterTimer;
g_TimeBetweenLastTwoGCs = _currentPerfCounterTimer - g_TotalTimeSinceLastGCEnd;

// Compute Time in GC
g_TotalTimeInGC = _currentPerfCounterTimer - g_TotalTimeInGC;
uint64_t _timeInGCBase = (_currentPerfCounterTimer - g_TotalTimeSinceLastGCEnd);

Expand All @@ -203,6 +214,10 @@ void GCHeap::UpdatePostGCCounters()
g_percentTimeInGCSinceLastGC = (int)(g_TotalTimeInGC * 100 / _timeInGCBase);
else
g_percentTimeInGCSinceLastGC = 0;

// Update per-generation time spent in GC in ms
uint32_t totalTimeInGCInMs = g_TotalTimeInGC * 1000 / _currentPerfCounterFreq;
g_GenerationLastGCDuration[condemned_gen] = totalTimeInGCInMs;
g_TotalTimeSinceLastGCEnd = _currentPerfCounterTimer;
}

Expand All @@ -216,6 +231,20 @@ size_t GCHeap::GetLastGCGenerationSize(int gen)
return g_GenerationSizes[gen];
}

size_t GCHeap::GetLastGCTimeBetweenGC(int gen)
{
sywhang marked this conversation as resolved.
Show resolved Hide resolved
if (gen < 0)
{
return g_TimeBetweenLastTwoGCs;
}
return g_GenerationTimeBetweenGCs[gen];
}

int GCHeap::GetGenerationLastGCDuration(int gen)
{
return (int)g_GenerationLastGCDuration[gen];
}

size_t GCHeap::GetCurrentObjSize()
{
return (totalSurvivedSize + gc_heap::get_total_allocated());
Expand Down
4 changes: 4 additions & 0 deletions src/coreclr/src/gc/gcimpl.h
Original file line number Diff line number Diff line change
Expand Up @@ -319,6 +319,10 @@ class GCHeap : public IGCHeapInternal

size_t GetLastGCGenerationSize(int gen);

size_t GetLastGCTimeBetweenGC(int gen);

int GetGenerationLastGCDuration(int gen);

virtual void Shutdown();
};

Expand Down
4 changes: 4 additions & 0 deletions src/coreclr/src/gc/gcinterface.h
Original file line number Diff line number Diff line change
Expand Up @@ -715,6 +715,10 @@ class IGCHeap {

virtual size_t GetLastGCGenerationSize(int gen) = 0;

virtual size_t GetLastGCTimeBetweenGC(int gen) = 0;

virtual int GetGenerationLastGCDuration(int gen) = 0;

/*
===========================================================================
Miscellaneous routines used by the VM.
Expand Down
16 changes: 16 additions & 0 deletions src/coreclr/src/vm/comutilnative.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -969,6 +969,22 @@ FCIMPL1(UINT64, GCInterface::GetGenerationSize, int gen)
}
FCIMPLEND

FCIMPL1(UINT64, GCInterface::GetGenerationTimeBetweenGC, int gen)
{
FCALL_CONTRACT;

return (UINT64)(GCHeapUtilities::GetGCHeap()->GetLastGCTimeBetweenGC(gen));
}
FCIMPLEND

FCIMPL1(int, GCInterface::GetGenerationLastGCDuration, int gen)
{
FCALL_CONTRACT;

return GCHeapUtilities::GetGCHeap()->GetGenerationLastGCDuration(gen);
}
FCIMPLEND

/*================================GetTotalMemory================================
**Action: Returns the total number of bytes in use
**Returns: The total number of bytes in use
Expand Down
2 changes: 2 additions & 0 deletions src/coreclr/src/vm/comutilnative.h
Original file line number Diff line number Diff line change
Expand Up @@ -159,6 +159,8 @@ class GCInterface {
static FCDECL0(UINT64, GetSegmentSize);
static FCDECL0(int, GetLastGCPercentTimeInGC);
static FCDECL1(UINT64, GetGenerationSize, int gen);
static FCDECL1(UINT64, GetGenerationTimeBetweenGC, int gen);
static FCDECL1(int, GetGenerationLastGCDuration, int gen);
static
INT64 QCALLTYPE GetTotalMemory();

Expand Down
2 changes: 2 additions & 0 deletions src/coreclr/src/vm/ecalllist.h
Original file line number Diff line number Diff line change
Expand Up @@ -721,6 +721,8 @@ FCFuncStart(gGCInterfaceFuncs)
FCFuncElement("GetSegmentSize", GCInterface::GetSegmentSize)
FCFuncElement("GetLastGCPercentTimeInGC", GCInterface::GetLastGCPercentTimeInGC)
FCFuncElement("GetGenerationSize", GCInterface::GetGenerationSize)
FCFuncElement("GetGenerationTimeBetweenGC", GCInterface::GetGenerationTimeBetweenGC)
FCFuncElement("GetGenerationLastGCDuration", GCInterface::GetGenerationLastGCDuration)
QCFuncElement("_AddMemoryPressure", GCInterface::_AddMemoryPressure)
QCFuncElement("_RemoveMemoryPressure", GCInterface::_RemoveMemoryPressure)
FCFuncElement("GetGeneration", GCInterface::GetGeneration)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ internal sealed class RuntimeEventSource : EventSource
private IncrementingPollingCounter? _completedItemsCounter;
private IncrementingPollingCounter? _allocRateCounter;
private PollingCounter? _timerCounter;
private PollingCounter? _fragmentationCounter;

#if !MONO
private IncrementingPollingCounter? _exceptionCounter;
Expand All @@ -39,6 +40,13 @@ internal sealed class RuntimeEventSource : EventSource
private PollingCounter? _assemblyCounter;
private PollingCounter? _ilBytesJittedCounter;
private PollingCounter? _methodsJittedCounter;
private PollingCounter? _lastGen0GCTimeCounter;
private PollingCounter? _lastGen1GCTimeCounter;
private PollingCounter? _lastGen2GCTimeCounter;
private PollingCounter? _timeBetweenGCCounter;
private PollingCounter? _gen0TimeBetweenGCCounter;
private PollingCounter? _gen1TimeBetweenGCCounter;
private PollingCounter? _gen2TimeBetweenGCCounter;
#endif

public static void Initialize()
Expand Down Expand Up @@ -71,6 +79,10 @@ protected override void OnEventCommand(EventCommandEventArgs command)
_completedItemsCounter ??= new IncrementingPollingCounter("threadpool-completed-items-count", this, () => ThreadPool.CompletedWorkItemCount) { DisplayName = "ThreadPool Completed Work Item Count", DisplayRateTimeScale = new TimeSpan(0, 0, 1) };
_allocRateCounter ??= new IncrementingPollingCounter("alloc-rate", this, () => GC.GetTotalAllocatedBytes()) { DisplayName = "Allocation Rate", DisplayUnits = "B", DisplayRateTimeScale = new TimeSpan(0, 0, 1) };
_timerCounter ??= new PollingCounter("active-timer-count", this, () => Timer.ActiveCount) { DisplayName = "Number of Active Timers" };
_fragmentationCounter ??= new PollingCounter("gc-fragmentation", this, () => {
var gcInfo = GC.GetGCMemoryInfo();
return (double)(gcInfo.FragmentedBytes * 100 / gcInfo.HeapSizeBytes);
sywhang marked this conversation as resolved.
Show resolved Hide resolved
}) { DisplayName = "GC Fragmentation", DisplayUnits = "%" };
#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 All @@ -82,6 +94,13 @@ protected override void OnEventCommand(EventCommandEventArgs command)
_assemblyCounter ??= new PollingCounter("assembly-count", this, () => System.Reflection.Assembly.GetAssemblyCount()) { DisplayName = "Number of Assemblies Loaded" };
_ilBytesJittedCounter ??= new PollingCounter("il-bytes-jitted", this, () => System.Runtime.CompilerServices.RuntimeHelpers.GetILBytesJitted()) { DisplayName = "IL Bytes Jitted", DisplayUnits = "B" };
_methodsJittedCounter ??= new PollingCounter("methods-jitted-count", this, () => System.Runtime.CompilerServices.RuntimeHelpers.GetMethodsJittedCount()) { DisplayName = "Number of Methods Jitted" };
_lastGen0GCTimeCounter ??= new PollingCounter("mean-gen-0-duration", this, () => GC.GetGenerationLastGCDuration(0)) { DisplayName = "Last Gen 0 GC Pause Duration", DisplayUnits = "ms" };
_lastGen1GCTimeCounter ??= new PollingCounter("mean-gen-1-duration", this, () => GC.GetGenerationLastGCDuration(1)) { DisplayName = "Last Gen 1 GC Pause Duration", DisplayUnits = "ms" };
_lastGen2GCTimeCounter ??= new PollingCounter("mean-gen-2-duration", this, () => GC.GetGenerationLastGCDuration(2)) { DisplayName = "Last Gen 2 GC Pause Duration", DisplayUnits = "ms" };
_timeBetweenGCCounter ??= new PollingCounter("time-between-gc", this, () => GC.GetGenerationTimeBetweenGC(-1)) { DisplayName = "Time Between Last Two GCs", DisplayUnits = "ms" };
_gen0TimeBetweenGCCounter ??= new PollingCounter("time-between-gen-0-gc", this, () => GC.GetGenerationTimeBetweenGC(0)) { DisplayName = "Time Between Last Two Gen 0 GCs", DisplayUnits = "ms" };
_gen1TimeBetweenGCCounter ??= new PollingCounter("time-between-gen-1-gc", this, () => GC.GetGenerationTimeBetweenGC(1)) { DisplayName = "Time Between Last Two Gen 1 GCs", DisplayUnits = "ms" };
_gen2TimeBetweenGCCounter ??= new PollingCounter("time-between-gen-2-gc", this, () => GC.GetGenerationTimeBetweenGC(2)) { DisplayName = "Time Between Last Two Gen 2 GCs", DisplayUnits = "ms" };
#endif
}

Expand Down