Skip to content
This repository was archived by the owner on Jan 23, 2023. It is now read-only.
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
42 changes: 42 additions & 0 deletions src/System.Private.CoreLib/src/System/GC.cs
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,48 @@ public static GCMemoryInfo GetGCMemoryInfo()
fragmentedBytes: (long)(ulong)lastRecordedFragmentationBytes);
}

[DllImport(RuntimeHelpers.QCall, CharSet = CharSet.Ansi)]
private static extern void GetGCConfigurationVariable(string name, StringHandleOnStack retString);

//Based on GetCommandLineArgsNative
[MethodImpl(MethodImplOptions.InternalCall)]
private static extern string[] GetGCConfigurationVariables();

/// <summary>
/// Given the name of a GC configuration variable, get the set or default value.
/// </summary>
/// <remarks>
/// Returns `null` for an invalid name.
/// For a list of possible names, see the keys of `GetConfigurationVariables`.
///
/// Numeric are in decimal even though environment variables are specified using hexadecimal.
///
/// This reflects actually used values, not just explicit settings.
/// For example, if you set `System.GC.HeapCount` but not `System.GC.Server`,
/// the result for "HeapCount" would be "1" as workstation GC only uses 1 heap.
/// Similarly, "NoAffinitize" may be true if "HeapHardLimit" is set and HeapAffinitizeRanges is not.
/// </remarks>
public static string? GetConfigurationVariable(string name)
{
string result = "";
GetGCConfigurationVariable(name, new StringHandleOnStack(ref result));
return result == "" ? null : result;
}

/// <summary>
/// Returns all possible GC configuration variables and their values.
/// </summary>
/// <remarks>
/// See `GetConfigurationVariable` for description of how we get values.
/// </remarks>
public static IEnumerable<KeyValuePair<string, string>> GetConfigurationVariables()
{
foreach (string v in GetGCConfigurationVariables())
{
yield return new KeyValuePair<string, string>(v, GetConfigurationVariable(v)!);
}
}

[DllImport(RuntimeHelpers.QCall, CharSet = CharSet.Unicode)]
internal static extern int _StartNoGCRegion(long totalSize, bool lohSizeKnown, long lohSize, bool disallowFullBlockingGC);

Expand Down
115 changes: 115 additions & 0 deletions src/gc/gc.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -38803,6 +38803,121 @@ void GCHeap::GetMemoryInfo(uint64_t* highMemLoadThresholdBytes,
*lastRecordedFragmentationBytes = gc_heap::last_gc_fragmentation;
}

static const char* bool_to_str(const bool value)
{
const char* content = value ? "true" : "false";
const size_t len = strlen(content);
// + 1 for the '\0'
char* out = new (nothrow) char[len + 1];
if (out != nullptr)
{
strncpy(out, content, len + 1);
assert(out[len] == '\0' && strcmp(out, content) == 0);
}
return out;
}

static const char* int64_t_to_str(const int64_t value)
{
// 2**64 in base ten has 20 characters at most, + 1 for the '\0'
const size_t max_size = 21;
char* out = new (nothrow) char[max_size];
if (out != nullptr)
{
int n = _snprintf_s (out, max_size, _TRUNCATE, "%llu", (long long) value);
// -1 because n does not include the '\0'
assert (n <= max_size - 1);
}
return out;
}

static char const* const configuration_variables[11] =
{
"CpuGroup",
"HeapAffinitizeMask",
"HeapAffinitizeRanges",
"HeapCount",
"HeapHardLimit",
"HeapHardLimitPercent",
"HighMemoryPercent",
"LargePages",
"LOHThreshold",
"NoAffinitize",
"Server"
};

slice<char const* const> GCHeap::GetGCConfigurationVariables()
{
return slice<char const* const>::FromArray<11>(configuration_variables);
}

const char* GCHeap::GetGCConfigurationVariable(const char* key)
{
if (strcmp(key, "CpuGroup") == 0)
{
return bool_to_str(GCConfig::GetGCCpuGroup());
}
else if (strcmp(key, "HeapAffinitizeMask") == 0)
{
return int64_t_to_str(GCConfig::GetGCHeapAffinitizeMask());
}
else if (strcmp(key, "HeapAffinitizeRanges") == 0)
{
// GetGCHeapAffinitizeRanges returns a GCConfigStringHolder which will delete the string, but not if we Extract() it.
return GCConfig::GetGCHeapAffinitizeRanges().Extract();
}
else if (strcmp(key, "HeapCount") == 0)
{
#ifdef MULTIPLE_HEAPS
const size_t heapCount = gc_heap::n_heaps;
#else
const size_t heapCount = 1;
#endif
return int64_t_to_str(heapCount);
}
else if (strcmp(key, "HeapHardLimit") == 0)
{
return int64_t_to_str(gc_heap::heap_hard_limit);
}
else if (strcmp(key, "HeapHardLimitPercent") == 0)
{
return int64_t_to_str(GCConfig::GetGCHeapHardLimitPercent());
}
else if (strcmp(key, "HighMemoryPercent") == 0)
{
return int64_t_to_str(gc_heap::high_memory_load_th);
}
else if (strcmp(key, "LargePages") == 0)
{
return bool_to_str(gc_heap::use_large_pages_p);
}
else if (strcmp(key, "LOHThreshold") == 0)
{
return int64_t_to_str(loh_size_threshold);
}
else if (strcmp(key, "NoAffinitize") == 0)
{
#ifdef MULTIPLE_HEAPS
const bool noAffinitize = gc_heap::gc_thread_no_affinitize_p;
#else
const bool noAffinitize = false;
#endif
return bool_to_str(noAffinitize);
}
else if (strcmp(key, "Server") == 0)
{
#ifdef MULTIPLE_HEAPS
return bool_to_str(true);
#else
return bool_to_str(false);
#endif
}
else
{
return nullptr;
}
}

int GCHeap::GetGcLatencyMode()
{
return (int)(pGenGCHeap->settings.pause_mode);
Expand Down
6 changes: 6 additions & 0 deletions src/gc/gcconfig.h
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,12 @@ class GCConfigStringHolder

// Retrieves the wrapped config string.
const char* Get() const { return m_str; }

const char* Extract() {
const char* result = m_str;
m_str = nullptr;
return result;
}
};

// Note that the configs starting BGCFLTuningEnabled ending BGCG2RatioStep are for BGC servo
Expand Down
3 changes: 3 additions & 0 deletions src/gc/gcimpl.h
Original file line number Diff line number Diff line change
Expand Up @@ -178,6 +178,9 @@ class GCHeap : public IGCHeapInternal
size_t* lastRecordedHeapSizeBytes,
size_t* lastRecordedFragmentationBytes);

const char* GetGCConfigurationVariable(const char* key);
slice<char const* const> GetGCConfigurationVariables();

int GetGcLatencyMode();
int SetGcLatencyMode(int newLatencyMode);

Expand Down
29 changes: 29 additions & 0 deletions src/gc/gcinterface.h
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,32 @@
// mismatches can still interopate correctly, with some care.
#define GC_INTERFACE_MINOR_VERSION 1

template <typename T>
class slice {
T* _begin;
size_t _length;

slice(T* begin, size_t length) : _begin{begin}, _length{length} {}

public:
template <size_t size>
static slice<T> FromArray(T data[size])
{
return slice<T> { data, size };
}

size_t size() const
{
return _length;
}

const T& operator[](size_t index) const
{
assert(index < _length);
return _begin[index];
}
};

struct ScanContext;
struct gc_alloc_context;
class CrawlFrame;
Expand Down Expand Up @@ -612,6 +638,9 @@ class IGCHeap {
size_t* lastRecordedHeapSizeBytes,
size_t* lastRecordedFragmentationBytes) = 0;

virtual const char* GetGCConfigurationVariable(const char* key) = 0;
virtual slice<char const* const> GetGCConfigurationVariables() = 0;

// Gets the current GC latency mode.
virtual int GetGcLatencyMode() = 0;

Expand Down
2 changes: 1 addition & 1 deletion src/inc/clrconfigvalues.h
Original file line number Diff line number Diff line change
Expand Up @@ -315,7 +315,7 @@ RETAIL_CONFIG_STRING_INFO(EXTERNAL_GCName, W("GCName"), "")
RETAIL_CONFIG_DWORD_INFO_DIRECT_ACCESS(EXTERNAL_GCHeapHardLimit, W("GCHeapHardLimit"), "Specifies the maximum commit size for the GC heap")
RETAIL_CONFIG_DWORD_INFO_DIRECT_ACCESS(EXTERNAL_GCHeapHardLimitPercent, W("GCHeapHardLimitPercent"), "Specifies the GC heap usage as a percentage of the total memory")
RETAIL_CONFIG_STRING_INFO(EXTERNAL_GCHeapAffinitizeRanges, W("GCHeapAffinitizeRanges"), "Specifies list of processors for Server GC threads. The format is a comma separated list of processor numbers or ranges of processor numbers. Example: 1,3,5,7-9,12")
RETAIL_CONFIG_DWORD_INFO_DIRECT_ACCESS(EXTERNAL_GCLargePages, W("GCLargePages"), "Specifies whether large pages should be used when a heap hard limit is set")
RETAIL_CONFIG_DWORD_INFO(EXTERNAL_GCLargePages, W("GCLargePages"), 0, "Specifies whether large pages should be used when a heap hard limit is set")

///
/// IBC
Expand Down
1 change: 1 addition & 0 deletions src/inc/holder.h
Original file line number Diff line number Diff line change
Expand Up @@ -1028,6 +1028,7 @@ FORCEINLINE void DeleteArray(TYPE *value)

NEW_WRAPPER_TEMPLATE1(NewArrayHolder, DeleteArray<_TYPE>);
typedef NewArrayHolder<CHAR> AStringHolder;
typedef NewArrayHolder<const CHAR> AConstStringHolder;
typedef NewArrayHolder<WCHAR> WStringHolder;

//-----------------------------------------------------------------------------
Expand Down
4 changes: 3 additions & 1 deletion src/utilcode/util.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@
#include "corinfo.h"
#include "volatile.h"

#include "../inc/configuration.h"

#ifndef DACCESS_COMPILE
UINT32 g_nClrInstanceId = 0;
#endif //!DACCESS_COMPILE
Expand Down Expand Up @@ -989,7 +991,7 @@ DWORD LCM(DWORD u, DWORD v)
CONTRACTL_END;

#if !defined(FEATURE_REDHAWK) && (defined(_TARGET_AMD64_) || defined(_TARGET_ARM64_))
BOOL enableGCCPUGroups = CLRConfig::GetConfigValue(CLRConfig::EXTERNAL_GCCpuGroup) != 0;
BOOL enableGCCPUGroups = Configuration::GetKnobBooleanValue(W("System.GC.CpuGroup"), CLRConfig::EXTERNAL_GCCpuGroup);
Copy link
Member

Choose a reason for hiding this comment

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

This is adding a new official .runtime.json switch. Is it intentional?

Copy link
Member

Choose a reason for hiding this comment

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

yes, we want to allow all public facing GC configs to be specifiable via runtimeconfig.json.

Copy link
Member

Choose a reason for hiding this comment

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

  • It may be useful to get the list of the new public facing configs and make sure that they are designed well and have good names.
  • The new public facing GC configs are a new feature that should be approved for 3.0 by @MeiChin-Tsai at this point.

BOOL threadUseAllCpuGroups = CLRConfig::GetConfigValue(CLRConfig::EXTERNAL_Thread_UseAllCpuGroups) != 0;

if (!enableGCCPUGroups)
Expand Down
37 changes: 37 additions & 0 deletions src/vm/comutilnative.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -712,6 +712,43 @@ FCIMPL6(void, GCInterface::GetMemoryInfo, UINT64* highMemLoadThreshold, UINT64*
}
FCIMPLEND

void QCALLTYPE GCInterface::GetGCConfigurationVariable(const char* key, QCall::StringHandleOnStack result)
{
QCALL_CONTRACT;

BEGIN_QCALL;

AConstStringHolder value;
value = GCHeapUtilities::GetGCHeap()->GetGCConfigurationVariable(key);
if (value)
{
result.Set(value);
}

END_QCALL;
}

FCIMPL0(Object*, GCInterface::GetGCConfigurationVariables)
{
FCALL_CONTRACT;

slice<char const* const> configVariables = GCHeapUtilities::GetGCHeap()->GetGCConfigurationVariables();

PTRARRAYREF strArray = NULL;
HELPER_METHOD_FRAME_BEGIN_RET_1(strArray);
strArray = (PTRARRAYREF) AllocateObjectArray(static_cast<DWORD>(configVariables.size()), g_pStringClass);
for (unsigned int i = 0; i < configVariables.size(); i++)
{
STRINGREF str = StringObject::NewString(configVariables[i]); // This copies it
STRINGREF * destData = ((STRINGREF*)(strArray->GetDataPtr())) + i;
SetObjectReference((OBJECTREF*)destData, (OBJECTREF)str);
}

HELPER_METHOD_FRAME_END();
return OBJECTREFToObject(strArray);
}
FCIMPLEND

FCIMPL0(int, GCInterface::GetGcLatencyMode)
{
FCALL_CONTRACT;
Expand Down
4 changes: 4 additions & 0 deletions src/vm/comutilnative.h
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,10 @@ class GCInterface {
static FORCEINLINE UINT64 InterlockedSub(UINT64 *pMinuend, UINT64 subtrahend);

static FCDECL6(void, GetMemoryInfo, UINT64* highMemLoadThresholdBytes, UINT64* totalAvailableMemoryBytes, UINT64* lastRecordedMemLoadBytes, UINT32* lastRecordedMemLoadPct, size_t* lastRecordedHeapSizBytes, size_t* lastRecordedFragmentationBytes);
static void QCALLTYPE GetGCConfigurationVariable(const char* key, QCall::StringHandleOnStack result);
// The object should be a string[]
static FCDECL0(Object*, GetGCConfigurationVariables);

static FCDECL0(int, GetGcLatencyMode);
static FCDECL1(int, SetGcLatencyMode, int newLatencyMode);
static FCDECL0(int, GetLOHCompactionMode);
Expand Down
2 changes: 2 additions & 0 deletions src/vm/ecalllist.h
Original file line number Diff line number Diff line change
Expand Up @@ -741,6 +741,8 @@ FCFuncStart(gGCInterfaceFuncs)
FCFuncElement("_WaitForFullGCComplete", GCInterface::WaitForFullGCComplete)
FCFuncElement("_CollectionCount", GCInterface::CollectionCount)
FCFuncElement("GetMemoryInfo", GCInterface::GetMemoryInfo)
QCFuncElement("GetGCConfigurationVariable", GCInterface::GetGCConfigurationVariable)
FCFuncElement("GetGCConfigurationVariables", GCInterface::GetGCConfigurationVariables)
QCFuncElement("_StartNoGCRegion", GCInterface::StartNoGCRegion)
QCFuncElement("_EndNoGCRegion", GCInterface::EndNoGCRegion)
FCFuncElement("GetSegmentSize", GCInterface::GetSegmentSize)
Expand Down
8 changes: 7 additions & 1 deletion src/vm/eeconfig.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -814,12 +814,18 @@ fTrackDynamicMethodDebugInfo = CLRConfig::GetConfigValue(CLRConfig::UNSUPPORTED_
if (!iGCgen0size) iGCgen0size = GetConfigDWORD_DontUse_(CLRConfig::UNSUPPORTED_GCgen0size, iGCgen0size);
#endif //BIT64

const ULONGLONG ullHeapHardLimit = Configuration::GetKnobULONGLONGValue(W("System.GC.HeapHardLimit"));
ULONGLONG ullHeapHardLimit = Configuration::GetKnobULONGLONGValue(W("System.GC.HeapHardLimit"));
if (!ullHeapHardLimit) ullHeapHardLimit = GetConfigULONGLONG_DontUse_(CLRConfig::EXTERNAL_GCHeapHardLimit, 0);
iGCHeapHardLimit = FitsIn<size_t, ULONGLONG>(ullHeapHardLimit)
? static_cast<size_t>(ullHeapHardLimit)
: ClrSafeInt<size_t>::MaxInt();
iGCHeapHardLimitPercent = Configuration::GetKnobDWORDValue(W("System.GC.HeapHardLimitPercent"), 0);

iGCCpuGroup = Configuration::GetKnobBooleanValue(W("System.GC.CpuGroup"), CLRConfig::EXTERNAL_GCCpuGroup);
iGCHighMemoryPercent = Configuration::GetKnobDWORDValue(W("System.GC.HighMemoryPercent"), CLRConfig::EXTERNAL_GCHighMemPercent);
fGCLargePages = Configuration::GetKnobBooleanValue(W("System.GC.LargePages"), CLRConfig::EXTERNAL_GCLargePages);
Copy link
Member

Choose a reason for hiding this comment

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

The large pages come in multiple sizes. Do we need to design for that?

Copy link
Member

Choose a reason for hiding this comment

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

I would think we just let the users specify whether they want to use large pages or not and if so we will use as many larger large pages as we can instead of users having to worry about what size to specify


In reply to: 299312301 [](ancestors = 299312301)

lpszGCHeapAffinitizeRanges = Configuration::GetKnobStringValue(W("System.GC.HeapAffinitizeRanges"), CLRConfig::EXTERNAL_GCHeapAffinitizeRanges);

if (g_IGCHoardVM)
iGCHoardVM = g_IGCHoardVM;
else
Expand Down
9 changes: 9 additions & 0 deletions src/vm/eeconfig.h
Original file line number Diff line number Diff line change
Expand Up @@ -612,8 +612,12 @@ class EEConfig
int GetGCHeapCount() const {LIMITED_METHOD_CONTRACT; return iGCHeapCount;}
int GetGCNoAffinitize () const {LIMITED_METHOD_CONTRACT; return iGCNoAffinitize;}
size_t GetGCAffinityMask() const {LIMITED_METHOD_CONTRACT; return iGCAffinityMask;}
LPCWSTR GetGCHeapAffinitizeRanges() const {LIMITED_METHOD_CONTRACT; return lpszGCHeapAffinitizeRanges;}
int GetGCHighMemoryPercent() const {LIMITED_METHOD_CONTRACT; return iGCHighMemoryPercent;}
size_t GetGCHeapHardLimit() const {LIMITED_METHOD_CONTRACT; return iGCHeapHardLimit;}
int GetGCHeapHardLimitPercent() const {LIMITED_METHOD_CONTRACT; return iGCHeapHardLimitPercent;}
bool GetGCLargePages() const {LIMITED_METHOD_CONTRACT; return fGCLargePages;}
bool GetGCCpuGroup() const {LIMITED_METHOD_CONTRACT; return iGCCpuGroup;}

#ifdef GCTRIMCOMMIT

Expand Down Expand Up @@ -917,8 +921,13 @@ class EEConfig
int iGCHeapCount;
int iGCNoAffinitize;
size_t iGCAffinityMask;
bool iGCCpuGroup;
// this points to a string from GetKnobStringValue and should not be freed.
LPCWSTR lpszGCHeapAffinitizeRanges;
int iGCHighMemoryPercent;
size_t iGCHeapHardLimit;
int iGCHeapHardLimitPercent;
bool fGCLargePages;

#ifdef GCTRIMCOMMIT

Expand Down
Loading