From c1eea62d611a9e34ee05f3d39cae701b4894181e Mon Sep 17 00:00:00 2001 From: Andrew Au Date: Fri, 20 May 2022 14:09:19 -0700 Subject: [PATCH 1/5] GetTotalPauseDuration implementation (#68835) --- .../System.Private.CoreLib/src/System/GC.cs | 8 +++++ src/coreclr/gc/gc.cpp | 5 +++ src/coreclr/gc/gcimpl.h | 4 ++- src/coreclr/gc/gcinterface.h | 3 ++ src/coreclr/vm/comutilnative.cpp | 10 ++++++ src/coreclr/vm/comutilnative.h | 1 + src/coreclr/vm/ecalllist.h | 1 + .../System.Runtime/ref/System.Runtime.cs | 6 ++++ .../src/System/GC.Mono.cs | 5 +++ src/tests/GC/API/GC/GetTotalPauseDuration.cs | 35 +++++++++++++++++++ .../GC/API/GC/GetTotalPauseDuration.csproj | 16 +++++++++ 11 files changed, 93 insertions(+), 1 deletion(-) create mode 100644 src/tests/GC/API/GC/GetTotalPauseDuration.cs create mode 100644 src/tests/GC/API/GC/GetTotalPauseDuration.csproj diff --git a/src/coreclr/System.Private.CoreLib/src/System/GC.cs b/src/coreclr/System.Private.CoreLib/src/System/GC.cs index bcbf042fd1b24d..33594cca7170ae 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/GC.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/GC.cs @@ -719,5 +719,13 @@ public static T[] AllocateArray(int length, bool pinned = false) // T[] rathe return Unsafe.As(AllocateNewArray(typeof(T[]).TypeHandle.Value, length, flags)); } + + [MethodImpl(MethodImplOptions.InternalCall)] + private static extern long _GetTotalPauseDuration(); + + public static TimeSpan GetTotalPauseDuration() + { + return new TimeSpan(_GetTotalPauseDuration()); + } } } diff --git a/src/coreclr/gc/gc.cpp b/src/coreclr/gc/gc.cpp index 7400a2eae94882..df9a96b8dc0099 100644 --- a/src/coreclr/gc/gc.cpp +++ b/src/coreclr/gc/gc.cpp @@ -45045,6 +45045,11 @@ void GCHeap::GetMemoryInfo(uint64_t* highMemLoadThresholdBytes, #endif //_DEBUG } +int64_t GCHeap::GetTotalPauseDuration() +{ + return (int64_t)(gc_heap::total_suspended_time * 10); +} + uint32_t GCHeap::GetMemoryLoad() { uint32_t memory_load = 0; diff --git a/src/coreclr/gc/gcimpl.h b/src/coreclr/gc/gcimpl.h index 5f631642e11c76..1d470a0c8ca124 100644 --- a/src/coreclr/gc/gcimpl.h +++ b/src/coreclr/gc/gcimpl.h @@ -180,7 +180,9 @@ class GCHeap : public IGCHeapInternal bool* isConcurrent, uint64_t* genInfoRaw, uint64_t* pauseInfoRaw, - int kind);; + int kind); + + int64_t GetTotalPauseDuration(); uint32_t GetMemoryLoad(); diff --git a/src/coreclr/gc/gcinterface.h b/src/coreclr/gc/gcinterface.h index d7c08c44d9adce..03c56d49dbb531 100644 --- a/src/coreclr/gc/gcinterface.h +++ b/src/coreclr/gc/gcinterface.h @@ -925,6 +925,9 @@ class IGCHeap { IGCHeap() {} virtual ~IGCHeap() {} + + // Get the total paused duration + virtual int64_t GetTotalPauseDuration() = 0; }; #ifdef WRITE_BARRIER_CHECK diff --git a/src/coreclr/vm/comutilnative.cpp b/src/coreclr/vm/comutilnative.cpp index fec65f96e5928f..6b5bdde020abf6 100644 --- a/src/coreclr/vm/comutilnative.cpp +++ b/src/coreclr/vm/comutilnative.cpp @@ -674,6 +674,16 @@ UINT64 GCInterface::m_remPressure[MEM_PRESSURE_COUNT] = {0, 0, 0, 0}; // his // (m_iteration % MEM_PRESSURE_COUNT) is used as an index into m_addPressure and m_remPressure UINT GCInterface::m_iteration = 0; +FCIMPL0(INT64, GCInterface::GetTotalPauseDuration) +{ + FCALL_CONTRACT; + + FC_GC_POLL_NOT_NEEDED(); + + return GCHeapUtilities::GetGCHeap()->GetTotalPauseDuration(); +} +FCIMPLEND + FCIMPL2(void, GCInterface::GetMemoryInfo, Object* objUNSAFE, int kind) { FCALL_CONTRACT; diff --git a/src/coreclr/vm/comutilnative.h b/src/coreclr/vm/comutilnative.h index f59bbe7f66c118..666ed6aefcfc25 100644 --- a/src/coreclr/vm/comutilnative.h +++ b/src/coreclr/vm/comutilnative.h @@ -143,6 +143,7 @@ class GCInterface { static FORCEINLINE UINT64 InterlockedAdd(UINT64 *pAugend, UINT64 addend); static FORCEINLINE UINT64 InterlockedSub(UINT64 *pMinuend, UINT64 subtrahend); + static FCDECL0(INT64, GetTotalPauseDuration); static FCDECL2(void, GetMemoryInfo, Object* objUNSAFE, int kind); static FCDECL0(UINT32, GetMemoryLoad); static FCDECL0(int, GetGcLatencyMode); diff --git a/src/coreclr/vm/ecalllist.h b/src/coreclr/vm/ecalllist.h index 363ae6f6cfa7cc..8317386a22f722 100644 --- a/src/coreclr/vm/ecalllist.h +++ b/src/coreclr/vm/ecalllist.h @@ -706,6 +706,7 @@ FCFuncStart(gGCInterfaceFuncs) FCFuncElement("_WaitForFullGCComplete", GCInterface::WaitForFullGCComplete) FCFuncElement("_CollectionCount", GCInterface::CollectionCount) FCFuncElement("GetMemoryInfo", GCInterface::GetMemoryInfo) + FCFuncElement("_GetTotalPauseDuration", GCInterface::GetTotalPauseDuration) FCFuncElement("GetMemoryLoad", GCInterface::GetMemoryLoad) QCFuncElement("_StartNoGCRegion", GCInterface::StartNoGCRegion) QCFuncElement("_EndNoGCRegion", GCInterface::EndNoGCRegion) diff --git a/src/libraries/System.Runtime/ref/System.Runtime.cs b/src/libraries/System.Runtime/ref/System.Runtime.cs index 66aa431bb4861f..381de1e98f36d2 100644 --- a/src/libraries/System.Runtime/ref/System.Runtime.cs +++ b/src/libraries/System.Runtime/ref/System.Runtime.cs @@ -2950,6 +2950,12 @@ public static void SuppressFinalize(object obj) { } public static System.GCNotificationStatus WaitForFullGCComplete() { throw null; } public static System.GCNotificationStatus WaitForFullGCComplete(int millisecondsTimeout) { throw null; } public static void WaitForPendingFinalizers() { } + + /// + /// Gets the total amount of time paused in GC since the beginning of the process. + /// + /// The total amount of time paused in GC since the beginning of the process. + public static TimeSpan GetTotalPauseDuration() { return TimeSpan.Zero; } } public enum GCCollectionMode { diff --git a/src/mono/System.Private.CoreLib/src/System/GC.Mono.cs b/src/mono/System.Private.CoreLib/src/System/GC.Mono.cs index a92ebab49920d7..4a383c9efb7172 100644 --- a/src/mono/System.Private.CoreLib/src/System/GC.Mono.cs +++ b/src/mono/System.Private.CoreLib/src/System/GC.Mono.cs @@ -313,5 +313,10 @@ internal static int GetLastGCPercentTimeInGC() { return (int)EventPipeInternal.GetRuntimeCounterValue(EventPipeInternal.RuntimeCounters.GC_LAST_PERCENT_TIME_IN_GC); } + + public static TimeSpan GetTotalPauseDuration() + { + return TimeSpan.Zero; + } } } diff --git a/src/tests/GC/API/GC/GetTotalPauseDuration.cs b/src/tests/GC/API/GC/GetTotalPauseDuration.cs new file mode 100644 index 00000000000000..1435c3493aba92 --- /dev/null +++ b/src/tests/GC/API/GC/GetTotalPauseDuration.cs @@ -0,0 +1,35 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// Tests GC.GetTotalPauseDuration() + +using System; +using System.Diagnostics; + +public class Test_Collect +{ + public static int Main() + { + Stopwatch sw = Stopwatch.StartNew(); + GC.Collect(); + sw.Stop(); + TimeSpan elapsed = sw.Elapsed; + TimeSpan totalPauseDuration = GC.GetTotalPauseDuration(); + GCMemoryInfo memoryInfo = GC.GetGCMemoryInfo(); + TimeSpan lastGcDuration = memoryInfo.PauseDurations[0]; + + // These conditions assume the only GC in the process + // is the one we just triggered. This makes the test incompatible + // with any changes that might introduce extra GCs. + + if (TimeSpan.Zero < totalPauseDuration && + totalPauseDuration <= elapsed && + lastGcDuration == totalPauseDuration) + { + return 100; + } + else + { + return 101; + } + } +} diff --git a/src/tests/GC/API/GC/GetTotalPauseDuration.csproj b/src/tests/GC/API/GC/GetTotalPauseDuration.csproj new file mode 100644 index 00000000000000..358a5cea38bb5e --- /dev/null +++ b/src/tests/GC/API/GC/GetTotalPauseDuration.csproj @@ -0,0 +1,16 @@ + + + Exe + + true + + 0 + + + + PdbOnly + + + + + From 1899d2d6804f25c8d72b6c0c2779193146431076 Mon Sep 17 00:00:00 2001 From: Andrew Au Date: Mon, 10 Jul 2023 12:28:40 -0700 Subject: [PATCH 2/5] Avoid changing reference assembly --- src/libraries/System.Runtime/ref/System.Runtime.cs | 6 ------ 1 file changed, 6 deletions(-) diff --git a/src/libraries/System.Runtime/ref/System.Runtime.cs b/src/libraries/System.Runtime/ref/System.Runtime.cs index 381de1e98f36d2..66aa431bb4861f 100644 --- a/src/libraries/System.Runtime/ref/System.Runtime.cs +++ b/src/libraries/System.Runtime/ref/System.Runtime.cs @@ -2950,12 +2950,6 @@ public static void SuppressFinalize(object obj) { } public static System.GCNotificationStatus WaitForFullGCComplete() { throw null; } public static System.GCNotificationStatus WaitForFullGCComplete(int millisecondsTimeout) { throw null; } public static void WaitForPendingFinalizers() { } - - /// - /// Gets the total amount of time paused in GC since the beginning of the process. - /// - /// The total amount of time paused in GC since the beginning of the process. - public static TimeSpan GetTotalPauseDuration() { return TimeSpan.Zero; } } public enum GCCollectionMode { From 6cca632e0913a5e41baf38e642c2b8904258d7d1 Mon Sep 17 00:00:00 2001 From: Andrew Au Date: Mon, 10 Jul 2023 13:36:09 -0700 Subject: [PATCH 3/5] Attempt to silent the warning --- .../System.Runtime/src/MatchingRefApiCompatBaseline.txt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/libraries/System.Runtime/src/MatchingRefApiCompatBaseline.txt b/src/libraries/System.Runtime/src/MatchingRefApiCompatBaseline.txt index cbe8cabdbb2956..3dc456e29d36ea 100644 --- a/src/libraries/System.Runtime/src/MatchingRefApiCompatBaseline.txt +++ b/src/libraries/System.Runtime/src/MatchingRefApiCompatBaseline.txt @@ -24,4 +24,5 @@ CannotMakeMemberAbstract : Member 'public System.Boolean System.IO.FileSystemInf CannotMakeMemberAbstract : Member 'public System.String System.IO.FileSystemInfo.Name.get()' is abstract in the reference but is not abstract in the implementation. # C# generates backing fields for fixed buffers as public. TypesMustExist : Type 'System.IO.Enumeration.FileSystemEntry.<_fileNameBuffer>e__FixedBuffer' does not exist in the reference but it does exist in the implementation. - +# This API only exists on servicing builds after the commit that introduce it +MembersMustExist : Member 'public System.TimeSpan System.GC.GetTotalPauseDuration() does not exist in the reference but it does exist in the implementation. \ No newline at end of file From 2008ae8037cb395ec336caaf29e17051cfe6d9c8 Mon Sep 17 00:00:00 2001 From: Andrew Au Date: Mon, 10 Jul 2023 15:30:07 -0700 Subject: [PATCH 4/5] Update src/libraries/System.Runtime/src/MatchingRefApiCompatBaseline.txt Co-authored-by: Eric StJohn --- .../System.Runtime/src/MatchingRefApiCompatBaseline.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libraries/System.Runtime/src/MatchingRefApiCompatBaseline.txt b/src/libraries/System.Runtime/src/MatchingRefApiCompatBaseline.txt index 3dc456e29d36ea..542d29445b645c 100644 --- a/src/libraries/System.Runtime/src/MatchingRefApiCompatBaseline.txt +++ b/src/libraries/System.Runtime/src/MatchingRefApiCompatBaseline.txt @@ -25,4 +25,4 @@ CannotMakeMemberAbstract : Member 'public System.String System.IO.FileSystemInfo # C# generates backing fields for fixed buffers as public. TypesMustExist : Type 'System.IO.Enumeration.FileSystemEntry.<_fileNameBuffer>e__FixedBuffer' does not exist in the reference but it does exist in the implementation. # This API only exists on servicing builds after the commit that introduce it -MembersMustExist : Member 'public System.TimeSpan System.GC.GetTotalPauseDuration() does not exist in the reference but it does exist in the implementation. \ No newline at end of file +MembersMustExist : Member 'public System.TimeSpan System.GC.GetTotalPauseDuration()' does not exist in the reference but it does exist in the implementation. \ No newline at end of file From 7329f961805b15435a3a5a6213f26560f09d8c0e Mon Sep 17 00:00:00 2001 From: Andrew Au Date: Mon, 10 Jul 2023 16:17:28 -0700 Subject: [PATCH 5/5] Fix test case --- src/tests/GC/API/GC/GetTotalPauseDuration.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/tests/GC/API/GC/GetTotalPauseDuration.cs b/src/tests/GC/API/GC/GetTotalPauseDuration.cs index 1435c3493aba92..96ac42640c804b 100644 --- a/src/tests/GC/API/GC/GetTotalPauseDuration.cs +++ b/src/tests/GC/API/GC/GetTotalPauseDuration.cs @@ -4,6 +4,7 @@ using System; using System.Diagnostics; +using System.Reflection; public class Test_Collect { @@ -13,7 +14,7 @@ public static int Main() GC.Collect(); sw.Stop(); TimeSpan elapsed = sw.Elapsed; - TimeSpan totalPauseDuration = GC.GetTotalPauseDuration(); + TimeSpan totalPauseDuration = (TimeSpan)typeof(GC).GetMethod("GetTotalPauseDuration", BindingFlags.Public | BindingFlags.Static).Invoke(null, null); GCMemoryInfo memoryInfo = GC.GetGCMemoryInfo(); TimeSpan lastGcDuration = memoryInfo.PauseDurations[0];