Skip to content

Commit

Permalink
Added a new method to the GC API to Get the GC Configurations (#70514)
Browse files Browse the repository at this point in the history
Reviewed by: @cshung  @jkotas  @AaronRobinsonMSFT @Maoni0 
Co-authored-by: Aaron Robinson <arobins@microsoft.com>
  • Loading branch information
mrsharm committed Jul 2, 2022
1 parent 6b766cd commit 5391db5
Show file tree
Hide file tree
Showing 16 changed files with 373 additions and 81 deletions.
73 changes: 73 additions & 0 deletions src/coreclr/System.Private.CoreLib/src/System/GC.CoreCLR.cs
Original file line number Diff line number Diff line change
Expand Up @@ -733,9 +733,82 @@ public static T[] AllocateArray<T>(int length, bool pinned = false) // T[] rathe
[MethodImpl(MethodImplOptions.InternalCall)]
private static extern long _GetTotalPauseDuration();

/// <summary>
/// Gets the total amount of time paused in GC since the beginning of the process.
/// </summary>
/// <returns> The total amount of time paused in GC since the beginning of the process.</returns>
public static TimeSpan GetTotalPauseDuration()
{
return new TimeSpan(_GetTotalPauseDuration());
}

internal struct GCConfigurationContext
{
internal Dictionary<string, object> Configurations;
}

[UnmanagedCallersOnly]
private static unsafe void Callback(void* configurationContext, void* name, void* publicKey, GCConfigurationType type, long data)
{
// If the public key is null, it means that the corresponding configuration isn't publicly available
// and therefore, we shouldn't add it to the configuration dictionary to return to the user.
if (publicKey == null)
{
return;
}

Debug.Assert(name != null);
Debug.Assert(configurationContext != null);

ref GCConfigurationContext context = ref Unsafe.As<byte, GCConfigurationContext>(ref *(byte*)configurationContext);
Debug.Assert(context.Configurations != null);
Dictionary<string, object> configurationDictionary = context.Configurations!;

string nameAsString = Marshal.PtrToStringUTF8((IntPtr)name)!;
switch (type)
{
case GCConfigurationType.Int64:
configurationDictionary[nameAsString] = data;
break;

case GCConfigurationType.StringUtf8:
{
string? dataAsString = Marshal.PtrToStringUTF8((IntPtr)data);
configurationDictionary[nameAsString] = dataAsString ?? string.Empty;
break;
}

case GCConfigurationType.Boolean:
configurationDictionary[nameAsString] = data != 0;
break;
}
}

/// <summary>
/// Gets the Configurations used by the Garbage Collector. The value of these configurations used don't neccessarily have to be the same as the ones that are passed by the user.
/// For example for the "GCHeapCount" configuration, if the user supplies a value higher than the number of CPUs, the configuration that will be used is that of the number of CPUs.
/// <returns> A Read Only Dictionary with configuration names and values of the configuration as the keys and values of the dictionary, respectively.</returns>
/// </summary>
public static unsafe IReadOnlyDictionary<string, object> GetConfigurationVariables()
{
GCConfigurationContext context = new GCConfigurationContext
{
Configurations = new Dictionary<string, object>()
};

_EnumerateConfigurationValues(Unsafe.AsPointer(ref context), &Callback);
return context.Configurations!;
}

// Corresponding Enum for the managed side of things in gcinterface.h that indicates the type of the configuration.
internal enum GCConfigurationType
{
Int64,
StringUtf8,
Boolean
}

[LibraryImport(RuntimeHelpers.QCall, EntryPoint = "GCInterface_EnumerateConfigurationValues")]
internal static unsafe partial void _EnumerateConfigurationValues(void* configurationDictionary, delegate* unmanaged<void*, void*, void*, GCConfigurationType, long, void> callback);
}
}
25 changes: 24 additions & 1 deletion src/coreclr/gc/gc.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -13115,6 +13115,10 @@ HRESULT gc_heap::initialize_gc (size_t soh_segment_size,
{
gc_can_use_concurrent = false;
}

GCConfig::SetConcurrentGC(gc_can_use_concurrent);
#else //BACKGROUND_GC
GCConfig::SetConcurrentGC(false);
#endif //BACKGROUND_GC
#endif //WRITE_WATCH

Expand Down Expand Up @@ -14118,6 +14122,7 @@ gc_heap::init_gc_heap (int h_number)
#ifdef DOUBLY_LINKED_FL
current_sweep_seg = 0;
#endif //DOUBLY_LINKED_FL

#endif //BACKGROUND_GC

#ifdef GC_CONFIG_DRIVEN
Expand Down Expand Up @@ -43961,11 +43966,18 @@ HRESULT GCHeap::Initialize()
#endif //USE_REGIONS

#endif //HOST_64BIT
GCConfig::SetGCLargePages(gc_heap::use_large_pages_p);
GCConfig::SetGCHeapHardLimit(static_cast<int64_t>(gc_heap::heap_hard_limit));
GCConfig::SetGCHeapHardLimitSOH(static_cast<int64_t>(gc_heap::heap_hard_limit_oh[soh]));
GCConfig::SetGCHeapHardLimitLOH(static_cast<int64_t>(gc_heap::heap_hard_limit_oh[loh]));
GCConfig::SetGCHeapHardLimitPOH(static_cast<int64_t>(gc_heap::heap_hard_limit_oh[poh]));

uint32_t nhp = 1;
uint32_t nhp_from_config = 0;

#ifdef MULTIPLE_HEAPS

GCConfig::SetServerGC(true);
AffinitySet config_affinity_set;
GCConfigStringHolder cpu_index_ranges_holder(GCConfig::GetGCHeapAffinitizeRanges());

Expand All @@ -43976,6 +43988,7 @@ HRESULT GCHeap::Initialize()
}

const AffinitySet* process_affinity_set = GCToOSInterface::SetGCThreadsAffinitySet(config_affinity_mask, &config_affinity_set);
GCConfig::SetGCHeapAffinitizeMask(static_cast<int64_t>(config_affinity_mask));

if (process_affinity_set->IsEmpty())
{
Expand Down Expand Up @@ -44112,6 +44125,8 @@ HRESULT GCHeap::Initialize()
}
#endif //!USE_REGIONS

GCConfig::SetHeapCount(static_cast<int64_t>(nhp));

#ifdef USE_REGIONS
// REGIONS TODO:
// soh_segment_size is used by a few places, I'm setting it temporarily and will
Expand Down Expand Up @@ -44236,8 +44251,11 @@ HRESULT GCHeap::Initialize()
dynamic_data* gen0_dd = hp->dynamic_data_of (0);
gc_heap::min_gen0_balance_delta = (dd_min_size (gen0_dd) >> 3);

bool can_use_cpu_groups = GCToOSInterface::CanEnableGCCPUGroups();
GCConfig::SetGCCpuGroup(can_use_cpu_groups);

#ifdef HEAP_BALANCE_INSTRUMENTATION
cpu_group_enabled_p = GCToOSInterface::CanEnableGCCPUGroups();
cpu_group_enabled_p = can_use_cpu_groups;

if (!GCToOSInterface::GetNumaInfo (&total_numa_nodes_on_machine, &procs_per_numa_node))
{
Expand Down Expand Up @@ -46438,6 +46456,11 @@ int64_t GCHeap::GetTotalPauseDuration()
return (int64_t)(gc_heap::total_suspended_time * 10);
}

void GCHeap::EnumerateConfigurationValues(void* context, ConfigurationValueFunc configurationValueFunc)
{
GCConfig::EnumerateConfigurationValues(context, configurationValueFunc);
}

uint32_t GCHeap::GetMemoryLoad()
{
uint32_t memory_load = 0;
Expand Down
33 changes: 30 additions & 3 deletions src/coreclr/gc/gcconfig.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,16 +7,20 @@

#define BOOL_CONFIG(name, unused_private_key, unused_public_key, default, unused_doc) \
bool GCConfig::Get##name() { return s_##name; } \
bool GCConfig::s_##name = default;
void GCConfig::Set##name(bool value) { s_Updated##name = value; } \
bool GCConfig::s_##name = default; \
bool GCConfig::s_Updated##name = default;

#define INT_CONFIG(name, unused_private_key, unused_public_key, default, unused_doc) \
int64_t GCConfig::Get##name() { return s_##name; } \
int64_t GCConfig::s_##name = default;
void GCConfig::Set##name(int64_t value) { s_Updated##name = value; } \
int64_t GCConfig::s_##name = default; \
int64_t GCConfig::s_Updated##name = default;

// String configs are not cached because 1) they are rare and
// not on hot paths and 2) they involve transfers of ownership
// of EE-allocated strings, which is potentially complicated.
#define STRING_CONFIG(name, private_key, public_key, unused_doc) \
#define STRING_CONFIG(name, private_key, public_key, unused_doc) \
GCConfigStringHolder GCConfig::Get##name() \
{ \
const char* resultStr = nullptr; \
Expand All @@ -30,6 +34,29 @@ GC_CONFIGURATION_KEYS
#undef INT_CONFIG
#undef STRING_CONFIG

void GCConfig::EnumerateConfigurationValues(void* context, ConfigurationValueFunc configurationValueFunc)
{
#define INT_CONFIG(name, unused_private_key, public_key, default, unused_doc) \
configurationValueFunc(context, (void*)(#name), (void*)(public_key), GCConfigurationType::Int64, static_cast<int64_t>(s_Updated##name));

#define STRING_CONFIG(name, private_key, public_key, unused_doc) \
{ \
const char* resultStr = nullptr; \
GCToEEInterface::GetStringConfigValue(private_key, public_key, &resultStr); \
GCConfigStringHolder holder(resultStr); \
configurationValueFunc(context, (void*)(#name), (void*)(public_key), GCConfigurationType::StringUtf8, reinterpret_cast<int64_t>(resultStr)); \
}

#define BOOL_CONFIG(name, unused_private_key, public_key, default, unused_doc) \
configurationValueFunc(context, (void*)(#name), (void*)(public_key), GCConfigurationType::Boolean, static_cast<int64_t>(s_Updated##name));

GC_CONFIGURATION_KEYS

#undef BOOL_CONFIG
#undef INT_CONFIG
#undef STRING_CONFIG
}

void GCConfig::Initialize()
{
#define BOOL_CONFIG(name, private_key, public_key, default, unused_doc) \
Expand Down
Loading

0 comments on commit 5391db5

Please sign in to comment.