Add more runtime GC counters (dotnet/coreclr#24561)
authorSung Yoon Whang <suwhang@microsoft.com>
Sat, 25 May 2019 03:16:11 +0000 (20:16 -0700)
committerGitHub <noreply@github.com>
Sat, 25 May 2019 03:16:11 +0000 (20:16 -0700)
* Add Series/CounterType to CounterPayload and IncrementingCounterPayload

* merging with master

* Add Generation sizes counter

* Some cleanup

* Add allocation rate counter

* Fix build

* add Allocation Rate runtime counter

* Fix a potential div by zero exception

* Add back in code commented out

* Add LOH size counter

* Fix linux build

* GetTotalAllocated -> GetTotalAllocation

* PR feedback

* More cleanup + renaming per PR feedback

* undo comments

* more pr feedback

* Use existing GC.GetTotalAllocatedBytes API instead

* Remove duplicate GetTotalAllocation

* More PR feedback

* Fix x86 build

* Match type between C++/C#

* remove unused variables'

Commit migrated from https://github.com/dotnet/coreclr/commit/b676246c1dd880b7290a1313cdac309fe020aa6f

src/coreclr/src/System.Private.CoreLib/src/System/Diagnostics/Eventing/RuntimeEventSource.cs
src/coreclr/src/System.Private.CoreLib/src/System/GC.cs
src/coreclr/src/gc/gcee.cpp
src/coreclr/src/gc/gcimpl.h
src/coreclr/src/gc/gcinterface.h
src/coreclr/src/vm/comutilnative.cpp
src/coreclr/src/vm/comutilnative.h
src/coreclr/src/vm/ecalllist.h

index 01366c1..2011101 100644 (file)
@@ -24,6 +24,12 @@ namespace System.Diagnostics.Tracing
         private IncrementingPollingCounter? _monitorContentionCounter;
         private PollingCounter? _threadPoolQueueCounter;
         private IncrementingPollingCounter? _completedItemsCounter;
+        private PollingCounter? _gcTimeCounter;
+        private PollingCounter? _gen0SizeCounter;
+        private PollingCounter? _gen1SizeCounter;
+        private PollingCounter? _gen2SizeCounter;
+        private PollingCounter? _lohSizeCounter;
+        private IncrementingPollingCounter? _allocRateCounter;
         private PollingCounter? _assemblyCounter;
 
         private const int EnabledPollingIntervalMilliseconds = 1000; // 1 second
@@ -57,6 +63,12 @@ namespace System.Diagnostics.Tracing
                 _monitorContentionCounter = _monitorContentionCounter ?? new IncrementingPollingCounter("monitor-lock-contention-count", this, () => Monitor.LockContentionCount) { DisplayName = "Monitor Lock Contention Count", DisplayRateTimeScale = new TimeSpan(0, 0, 1) }; 
                 _threadPoolQueueCounter = _threadPoolQueueCounter ?? new PollingCounter("threadpool-queue-length", this, () => ThreadPool.PendingWorkItemCount) { DisplayName = "ThreadPool Queue Length" };
                 _completedItemsCounter = _completedItemsCounter ?? new IncrementingPollingCounter("threadpool-completed-items-count", this, () => ThreadPool.CompletedWorkItemCount) { DisplayName = "ThreadPool Completed Work Item Count", DisplayRateTimeScale = new TimeSpan(0, 0, 1) };
+                _gcTimeCounter = _gcTimeCounter ?? new PollingCounter("time-in-gc", this, () => GC.GetLastGCPercentTimeInGC()) { DisplayName = "Time in GC" };
+                _gen0SizeCounter = _gen0SizeCounter ?? new PollingCounter("gen-0-size", this, () => GC.GetGenerationSize(0)) { DisplayName = "Gen 0 Size" };
+                _gen1SizeCounter = _gen1SizeCounter ?? new PollingCounter("gen-1-size", this, () => GC.GetGenerationSize(1)) { DisplayName = "Gen 1 Size" };
+                _gen2SizeCounter = _gen2SizeCounter ?? new PollingCounter("gen-2-size", this, () => GC.GetGenerationSize(2)) { DisplayName = "Gen 2 Size" };
+                _lohSizeCounter = _lohSizeCounter ?? new PollingCounter("loh-size", this, () => GC.GetGenerationSize(3)) { DisplayName = "LOH Size" };
+                _allocRateCounter = _allocRateCounter ?? new IncrementingPollingCounter("alloc-rate", this, () => GC.GetTotalAllocatedBytes()) { DisplayName = "Allocation Rate", DisplayRateTimeScale = new TimeSpan(0, 0, 1) };
                 _assemblyCounter = _assemblyCounter ?? new PollingCounter("assembly-count", this, () => System.Reflection.Assembly.GetAssemblyCount()) { DisplayName = "Number of Assemblies Loaded" };
             }
         }
index 501178f..a5ea093 100644 (file)
@@ -103,6 +103,12 @@ namespace System
         [MethodImplAttribute(MethodImplOptions.InternalCall)]
         internal static extern ulong GetSegmentSize();
 
+        [MethodImplAttribute(MethodImplOptions.InternalCall)]
+        internal static extern int GetLastGCPercentTimeInGC();
+
+        [MethodImplAttribute(MethodImplOptions.InternalCall)]
+        internal static extern ulong GetGenerationSize(int gen);
+
         [DllImport(JitHelpers.QCall, CharSet = CharSet.Unicode)]
         private static extern void _AddMemoryPressure(ulong bytesAllocated);
 
index 68310d5..e8bbdfa 100644 (file)
 
 #ifndef DACCESS_COMPILE
 
-#ifdef ENABLE_PERF_COUNTERS
-PERF_COUNTER_TIMER_PRECISION g_TotalTimeInGC = 0;
-PERF_COUNTER_TIMER_PRECISION g_TotalTimeSinceLastGCEnd = 0;
-#endif
+uint64_t g_TotalTimeInGC = 0;
+uint64_t g_TotalTimeSinceLastGCEnd = 0;
+
+uint32_t g_percentTimeInGCSinceLastGC = 0;
 
-#if defined(ENABLE_PERF_COUNTERS) || defined(FEATURE_EVENT_TRACE)
 size_t g_GenerationSizes[NUMBERGENERATIONS];
 size_t g_GenerationPromotedSizes[NUMBERGENERATIONS];
-#endif // ENABLE_PERF_COUNTERS || FEATURE_EVENT_TRACE
 
 void GCHeap::UpdatePreGCCounters()
 {
-#if defined(ENABLE_PERF_COUNTERS)
 #ifdef MULTIPLE_HEAPS
     gc_heap* hp = 0;
 #else
@@ -35,7 +32,7 @@ void GCHeap::UpdatePreGCCounters()
     size_t allocation_3 = 0; 
     
     // Publish perf stats
-    g_TotalTimeInGC = GET_CYCLE_COUNT();
+    g_TotalTimeInGC = GCToOSInterface::QueryPerformanceCounter();
 
 #ifdef MULTIPLE_HEAPS
     int hn = 0;
@@ -60,25 +57,6 @@ void GCHeap::UpdatePreGCCounters()
         
 #endif //MULTIPLE_HEAPS
 
-    GetPerfCounters().m_GC.cbAlloc += allocation_0;
-    GetPerfCounters().m_GC.cbAlloc += allocation_3;
-    GetPerfCounters().m_GC.cbLargeAlloc += allocation_3;
-
-#ifdef _PREFAST_
-    // prefix complains about us dereferencing hp in wks build even though we only access static members
-    // this way. not sure how to shut it up except for this ugly workaround:
-    PREFIX_ASSUME( hp != NULL);
-#endif //_PREFAST_
-    if (hp->settings.reason == reason_induced IN_STRESS_HEAP( && !hp->settings.stress_induced))
-    {
-        GetPerfCounters().m_GC.cInducedGCs++;
-    }
-
-    GetPerfCounters().m_Security.timeRTchecks = 0;
-    GetPerfCounters().m_Security.timeRTchecksBase = 1; // To avoid divide by zero
-
-#endif //ENABLE_PERF_COUNTERS
-
 #ifdef MULTIPLE_HEAPS
         //take the first heap....
     gc_mechanisms *pSettings = &gc_heap::g_heaps[0]->settings;
@@ -118,7 +96,7 @@ void GCHeap::UpdatePostGCCounters()
     // The following is for instrumentation.
     //
     // Calculate the common ones for ETW and perf counters.
-#if defined(ENABLE_PERF_COUNTERS) || defined(FEATURE_EVENT_TRACE)
+#if defined(FEATURE_EVENT_TRACE)
 #ifdef MULTIPLE_HEAPS
     //take the first heap....
     gc_heap* hp1 = gc_heap::g_heaps[0];
@@ -194,7 +172,7 @@ void GCHeap::UpdatePostGCCounters()
         }
 #endif //MULTIPLE_HEAPS
     }
-#endif //ENABLE_PERF_COUNTERS || FEATURE_EVENT_TRACE
+#endif //FEATURE_EVENT_TRACE
 
 #ifdef FEATURE_EVENT_TRACE
     g_theGCHeap->DiagDescrGenerations([](void*, int generation, uint8_t* rangeStart, uint8_t* rangeEnd, uint8_t* rangeEndReserved)
@@ -225,112 +203,39 @@ void GCHeap::UpdatePostGCCounters()
         static_cast<uint32_t>(total_num_pinned_objects),
         total_num_sync_blocks,
         static_cast<uint32_t>(total_num_gc_handles));
-#endif // FEATURE_EVENT_TRACE
 
-#if defined(ENABLE_PERF_COUNTERS)
-    for (int gen_index = 0; gen_index <= (max_generation+1); gen_index++)
-    {
-        _ASSERTE(FitsIn<size_t>(g_GenerationSizes[gen_index]));
-        _ASSERTE(FitsIn<size_t>(g_GenerationPromotedSizes[gen_index]));
-
-        if (gen_index == (max_generation+1))
-        {
-            GetPerfCounters().m_GC.cLrgObjSize = static_cast<size_t>(g_GenerationSizes[gen_index]);
-        }
-        else
-        {
-            GetPerfCounters().m_GC.cGenHeapSize[gen_index] = ((gen_index == 0) ? 
-                                                                youngest_budget : 
-                                                                static_cast<size_t>(g_GenerationSizes[gen_index]));
-        }
-
-        // the perf counters only count the promoted size for gen0 and gen1.
-        if (gen_index < max_generation)
-        {
-            GetPerfCounters().m_GC.cbPromotedMem[gen_index] = static_cast<size_t>(g_GenerationPromotedSizes[gen_index]);
-        }
-
-        if (gen_index <= max_generation)
-        {
-            GetPerfCounters().m_GC.cGenCollections[gen_index] =
-                dd_collection_count (hp1->dynamic_data_of (gen_index));
-        }
-    }
-
-    // Committed and reserved memory 
-    {
-        size_t committed_mem = 0;
-        size_t reserved_mem = 0;
-#ifdef MULTIPLE_HEAPS
-        int hn = 0;
-        for (hn = 0; hn < gc_heap::n_heaps; hn++)
-        {
-            gc_heap* hp = gc_heap::g_heaps [hn];
-#else
-            gc_heap* hp = pGenGCHeap;
-            {
-#endif //MULTIPLE_HEAPS
-                heap_segment* seg = generation_start_segment (hp->generation_of (max_generation));
-                while (seg)
-                {
-                    committed_mem += heap_segment_committed (seg) - heap_segment_mem (seg);
-                    reserved_mem += heap_segment_reserved (seg) - heap_segment_mem (seg);
-                    seg = heap_segment_next (seg);
-                }
-                //same for large segments
-                seg = generation_start_segment (hp->generation_of (max_generation + 1));
-                while (seg)
-                {
-                    committed_mem += heap_segment_committed (seg) - 
-                        heap_segment_mem (seg);
-                    reserved_mem += heap_segment_reserved (seg) - 
-                        heap_segment_mem (seg);
-                    seg = heap_segment_next (seg);
-                }
-#ifdef MULTIPLE_HEAPS
-            }
-#else
-        }
-#endif //MULTIPLE_HEAPS
-
-        GetPerfCounters().m_GC.cTotalCommittedBytes = committed_mem;
-        GetPerfCounters().m_GC.cTotalReservedBytes = reserved_mem;
-    }
+#endif // FEATURE_EVENT_TRACE
 
-    _ASSERTE(FitsIn<size_t>(HeapInfo.HeapStats.FinalizationPromotedSize));
-    _ASSERTE(FitsIn<size_t>(HeapInfo.HeapStats.FinalizationPromotedCount));
-    GetPerfCounters().m_GC.cbPromotedFinalizationMem = static_cast<size_t>(HeapInfo.HeapStats.FinalizationPromotedSize);
-    GetPerfCounters().m_GC.cSurviveFinalize = static_cast<size_t>(HeapInfo.HeapStats.FinalizationPromotedCount);
-    
     // Compute Time in GC
-    PERF_COUNTER_TIMER_PRECISION _currentPerfCounterTimer = GET_CYCLE_COUNT();
+    uint64_t _currentPerfCounterTimer = GCToOSInterface::QueryPerformanceCounter();
 
     g_TotalTimeInGC = _currentPerfCounterTimer - g_TotalTimeInGC;
-    PERF_COUNTER_TIMER_PRECISION _timeInGCBase = (_currentPerfCounterTimer - g_TotalTimeSinceLastGCEnd);
+    uint64_t _timeInGCBase = (_currentPerfCounterTimer - g_TotalTimeSinceLastGCEnd);
 
     if (_timeInGCBase < g_TotalTimeInGC)
         g_TotalTimeInGC = 0;        // isn't likely except on some SMP machines-- perhaps make sure that
                                     //  _timeInGCBase >= g_TotalTimeInGC by setting affinity in GET_CYCLE_COUNT
-                                    
-    while (_timeInGCBase > UINT_MAX) 
+
+    while (_timeInGCBase > UINT32_MAX) 
     {
         _timeInGCBase = _timeInGCBase >> 8;
         g_TotalTimeInGC = g_TotalTimeInGC >> 8;
     }
 
-    // Update Total Time    
-    GetPerfCounters().m_GC.timeInGC = (uint32_t)g_TotalTimeInGC;
-    GetPerfCounters().m_GC.timeInGCBase = (uint32_t)_timeInGCBase;
+    // Update percent time spent in GC
+    g_percentTimeInGCSinceLastGC = (int)(g_TotalTimeInGC * 100 / _timeInGCBase);
 
-    if (!GetPerfCounters().m_GC.cProcessID)
-        GetPerfCounters().m_GC.cProcessID = (size_t)GetCurrentProcessId();
-    
     g_TotalTimeSinceLastGCEnd = _currentPerfCounterTimer;
+}
+
+int GCHeap::GetLastGCPercentTimeInGC()
+{
+    return (int)(g_percentTimeInGCSinceLastGC);
+}
 
-    GetPerfCounters().m_GC.cPinnedObj = total_num_pinned_objects;
-    GetPerfCounters().m_GC.cHandles = total_num_gc_handles;
-    GetPerfCounters().m_GC.cSinkBlocks = total_num_sync_blocks;
-#endif //ENABLE_PERF_COUNTERS
+size_t GCHeap::GetLastGCGenerationSize(int gen)
+{
+    return g_GenerationSizes[gen];
 }
 
 size_t GCHeap::GetCurrentObjSize()
index b3aeb36..b1d4e07 100644 (file)
@@ -306,6 +306,10 @@ protected:
 
 public:
     Object * NextObj (Object * object);
+
+    int GetLastGCPercentTimeInGC();
+
+    size_t GetLastGCGenerationSize(int gen);
 };
 
 #endif  // GCIMPL_H_
index 083e84f..6d09a62 100644 (file)
@@ -674,6 +674,10 @@ public:
     // Indicates that an object's finalizer should be run upon the object's collection.
     virtual bool RegisterForFinalization(int gen, Object* obj) = 0;
 
+    virtual int GetLastGCPercentTimeInGC() = 0;
+
+    virtual size_t GetLastGCGenerationSize(int gen) = 0;
+
     /*
     ===========================================================================
     Miscellaneous routines used by the VM.
index d745c97..5d65956 100644 (file)
@@ -1165,6 +1165,22 @@ FCIMPL1(int, GCInterface::GetGenerationWR, LPVOID handle)
 }
 FCIMPLEND
 
+FCIMPL0(int, GCInterface::GetLastGCPercentTimeInGC)
+{
+    FCALL_CONTRACT;
+
+    return GCHeapUtilities::GetGCHeap()->GetLastGCPercentTimeInGC();
+}
+FCIMPLEND
+
+FCIMPL1(UINT64, GCInterface::GetGenerationSize, int gen)
+{
+    FCALL_CONTRACT;
+
+    return (UINT64)(GCHeapUtilities::GetGCHeap()->GetLastGCGenerationSize(gen));
+}
+FCIMPLEND
+
 /*================================GetTotalMemory================================
 **Action: Returns the total number of bytes in use
 **Returns: The total number of bytes in use
index 46ea7ff..d009d55 100644 (file)
@@ -122,7 +122,8 @@ public:
     static FCDECL1(int,     GetGenerationWR, LPVOID handle);
     static FCDECL1(int,     GetGeneration, Object* objUNSAFE);
     static FCDECL0(UINT64,  GetSegmentSize);
-
+    static FCDECL0(int,     GetLastGCPercentTimeInGC);
+    static FCDECL1(UINT64,  GetGenerationSize, int gen);
     static 
     INT64 QCALLTYPE GetTotalMemory();
 
index b69b52e..343d739 100644 (file)
@@ -769,6 +769,8 @@ FCFuncStart(gGCInterfaceFuncs)
     QCFuncElement("_StartNoGCRegion", GCInterface::StartNoGCRegion)
     QCFuncElement("_EndNoGCRegion", GCInterface::EndNoGCRegion)
     FCFuncElement("GetSegmentSize", GCInterface::GetSegmentSize)
+    FCFuncElement("GetLastGCPercentTimeInGC", GCInterface::GetLastGCPercentTimeInGC)
+    FCFuncElement("GetGenerationSize", GCInterface::GetGenerationSize)
     QCFuncElement("_AddMemoryPressure", GCInterface::_AddMemoryPressure)
     QCFuncElement("_RemoveMemoryPressure", GCInterface::_RemoveMemoryPressure)
     FCFuncElement("GetGeneration", GCInterface::GetGeneration)