Bug 1771315 - Add a command line option to the shell to set GC parameters r=sfink

This moves the parameter names and other data to a header file and adds a
command line option --gc-param.

Differential Revision: https://phabricator.services.mozilla.com/D147428
This commit is contained in:
Jon Coppeard 2022-05-27 09:06:03 +00:00
parent 2249f5ac31
commit 51e87f5456
4 changed files with 155 additions and 75 deletions

View File

@ -56,6 +56,7 @@
#include "frontend/BytecodeCompiler.h" // frontend::ParseModuleToExtensibleStencil
#include "frontend/CompilationStencil.h" // frontend::CompilationStencil
#include "gc/Allocator.h"
#include "gc/GC.h"
#include "gc/Zone.h"
#include "jit/BaselineJIT.h"
#include "jit/Disassemble.h"
@ -698,63 +699,6 @@ static bool MinorGC(JSContext* cx, unsigned argc, Value* vp) {
return true;
}
#define FOR_EACH_GC_PARAM(_) \
_("maxBytes", JSGC_MAX_BYTES, true) \
_("minNurseryBytes", JSGC_MIN_NURSERY_BYTES, true) \
_("maxNurseryBytes", JSGC_MAX_NURSERY_BYTES, true) \
_("gcBytes", JSGC_BYTES, false) \
_("nurseryBytes", JSGC_NURSERY_BYTES, false) \
_("gcNumber", JSGC_NUMBER, false) \
_("majorGCNumber", JSGC_MAJOR_GC_NUMBER, false) \
_("minorGCNumber", JSGC_MINOR_GC_NUMBER, false) \
_("incrementalGCEnabled", JSGC_INCREMENTAL_GC_ENABLED, true) \
_("perZoneGCEnabled", JSGC_PER_ZONE_GC_ENABLED, true) \
_("unusedChunks", JSGC_UNUSED_CHUNKS, false) \
_("totalChunks", JSGC_TOTAL_CHUNKS, false) \
_("sliceTimeBudgetMS", JSGC_SLICE_TIME_BUDGET_MS, true) \
_("markStackLimit", JSGC_MARK_STACK_LIMIT, true) \
_("highFrequencyTimeLimit", JSGC_HIGH_FREQUENCY_TIME_LIMIT, true) \
_("smallHeapSizeMax", JSGC_SMALL_HEAP_SIZE_MAX, true) \
_("largeHeapSizeMin", JSGC_LARGE_HEAP_SIZE_MIN, true) \
_("highFrequencySmallHeapGrowth", JSGC_HIGH_FREQUENCY_SMALL_HEAP_GROWTH, \
true) \
_("highFrequencyLargeHeapGrowth", JSGC_HIGH_FREQUENCY_LARGE_HEAP_GROWTH, \
true) \
_("lowFrequencyHeapGrowth", JSGC_LOW_FREQUENCY_HEAP_GROWTH, true) \
_("allocationThreshold", JSGC_ALLOCATION_THRESHOLD, true) \
_("smallHeapIncrementalLimit", JSGC_SMALL_HEAP_INCREMENTAL_LIMIT, true) \
_("largeHeapIncrementalLimit", JSGC_LARGE_HEAP_INCREMENTAL_LIMIT, true) \
_("minEmptyChunkCount", JSGC_MIN_EMPTY_CHUNK_COUNT, true) \
_("maxEmptyChunkCount", JSGC_MAX_EMPTY_CHUNK_COUNT, true) \
_("compactingEnabled", JSGC_COMPACTING_ENABLED, true) \
_("minLastDitchGCPeriod", JSGC_MIN_LAST_DITCH_GC_PERIOD, true) \
_("nurseryFreeThresholdForIdleCollection", \
JSGC_NURSERY_FREE_THRESHOLD_FOR_IDLE_COLLECTION, true) \
_("nurseryFreeThresholdForIdleCollectionPercent", \
JSGC_NURSERY_FREE_THRESHOLD_FOR_IDLE_COLLECTION_PERCENT, true) \
_("nurseryTimeoutForIdleCollectionMS", \
JSGC_NURSERY_TIMEOUT_FOR_IDLE_COLLECTION_MS, true) \
_("pretenureThreshold", JSGC_PRETENURE_THRESHOLD, true) \
_("pretenureGroupThreshold", JSGC_PRETENURE_GROUP_THRESHOLD, true) \
_("zoneAllocDelayKB", JSGC_ZONE_ALLOC_DELAY_KB, true) \
_("mallocThresholdBase", JSGC_MALLOC_THRESHOLD_BASE, true) \
_("urgentThreshold", JSGC_URGENT_THRESHOLD_MB, true) \
_("chunkBytes", JSGC_CHUNK_BYTES, false) \
_("helperThreadRatio", JSGC_HELPER_THREAD_RATIO, true) \
_("maxHelperThreads", JSGC_MAX_HELPER_THREADS, true) \
_("helperThreadCount", JSGC_HELPER_THREAD_COUNT, false) \
_("systemPageSizeKB", JSGC_SYSTEM_PAGE_SIZE_KB, false)
static const struct ParamInfo {
const char* name;
JSGCParamKey param;
bool writable;
} paramMap[] = {
#define DEFINE_PARAM_INFO(name, key, writable) {name, key, writable},
FOR_EACH_GC_PARAM(DEFINE_PARAM_INFO)
#undef DEFINE_PARAM_INFO
};
#define PARAM_NAME_LIST_ENTRY(name, key, writable) " " name
#define GC_PARAMETER_ARGS_LIST FOR_EACH_GC_PARAM(PARAM_NAME_LIST_ENTRY)
@ -766,24 +710,19 @@ static bool GCParameter(JSContext* cx, unsigned argc, Value* vp) {
return false;
}
JSLinearString* linearStr = JS_EnsureLinearString(cx, str);
if (!linearStr) {
UniqueChars name = EncodeLatin1(cx, str);
if (!name) {
return false;
}
const auto* ptr = std::find_if(
std::begin(paramMap), std::end(paramMap), [&](const auto& param) {
return JS_LinearStringEqualsAscii(linearStr, param.name);
});
if (ptr == std::end(paramMap)) {
JSGCParamKey param;
bool writable;
if (!GetGCParameterInfo(name.get(), &param, &writable)) {
JS_ReportErrorASCII(
cx, "the first argument must be one of:" GC_PARAMETER_ARGS_LIST);
return false;
}
const ParamInfo& info = *ptr;
JSGCParamKey param = info.param;
// Request mode.
if (args.length() == 1) {
uint32_t value = JS_GetGCParameter(cx, param);
@ -791,9 +730,9 @@ static bool GCParameter(JSContext* cx, unsigned argc, Value* vp) {
return true;
}
if (!info.writable) {
if (!writable) {
JS_ReportErrorASCII(cx, "Attempt to change read-only parameter %s",
info.name);
name.get());
return false;
}

View File

@ -36,6 +36,58 @@ struct Cell;
} /* namespace gc */
// Define name, key and writability for the GC parameters.
#define FOR_EACH_GC_PARAM(_) \
_("maxBytes", JSGC_MAX_BYTES, true) \
_("minNurseryBytes", JSGC_MIN_NURSERY_BYTES, true) \
_("maxNurseryBytes", JSGC_MAX_NURSERY_BYTES, true) \
_("gcBytes", JSGC_BYTES, false) \
_("nurseryBytes", JSGC_NURSERY_BYTES, false) \
_("gcNumber", JSGC_NUMBER, false) \
_("majorGCNumber", JSGC_MAJOR_GC_NUMBER, false) \
_("minorGCNumber", JSGC_MINOR_GC_NUMBER, false) \
_("incrementalGCEnabled", JSGC_INCREMENTAL_GC_ENABLED, true) \
_("perZoneGCEnabled", JSGC_PER_ZONE_GC_ENABLED, true) \
_("unusedChunks", JSGC_UNUSED_CHUNKS, false) \
_("totalChunks", JSGC_TOTAL_CHUNKS, false) \
_("sliceTimeBudgetMS", JSGC_SLICE_TIME_BUDGET_MS, true) \
_("markStackLimit", JSGC_MARK_STACK_LIMIT, true) \
_("highFrequencyTimeLimit", JSGC_HIGH_FREQUENCY_TIME_LIMIT, true) \
_("smallHeapSizeMax", JSGC_SMALL_HEAP_SIZE_MAX, true) \
_("largeHeapSizeMin", JSGC_LARGE_HEAP_SIZE_MIN, true) \
_("highFrequencySmallHeapGrowth", JSGC_HIGH_FREQUENCY_SMALL_HEAP_GROWTH, \
true) \
_("highFrequencyLargeHeapGrowth", JSGC_HIGH_FREQUENCY_LARGE_HEAP_GROWTH, \
true) \
_("lowFrequencyHeapGrowth", JSGC_LOW_FREQUENCY_HEAP_GROWTH, true) \
_("allocationThreshold", JSGC_ALLOCATION_THRESHOLD, true) \
_("smallHeapIncrementalLimit", JSGC_SMALL_HEAP_INCREMENTAL_LIMIT, true) \
_("largeHeapIncrementalLimit", JSGC_LARGE_HEAP_INCREMENTAL_LIMIT, true) \
_("minEmptyChunkCount", JSGC_MIN_EMPTY_CHUNK_COUNT, true) \
_("maxEmptyChunkCount", JSGC_MAX_EMPTY_CHUNK_COUNT, true) \
_("compactingEnabled", JSGC_COMPACTING_ENABLED, true) \
_("minLastDitchGCPeriod", JSGC_MIN_LAST_DITCH_GC_PERIOD, true) \
_("nurseryFreeThresholdForIdleCollection", \
JSGC_NURSERY_FREE_THRESHOLD_FOR_IDLE_COLLECTION, true) \
_("nurseryFreeThresholdForIdleCollectionPercent", \
JSGC_NURSERY_FREE_THRESHOLD_FOR_IDLE_COLLECTION_PERCENT, true) \
_("nurseryTimeoutForIdleCollectionMS", \
JSGC_NURSERY_TIMEOUT_FOR_IDLE_COLLECTION_MS, true) \
_("pretenureThreshold", JSGC_PRETENURE_THRESHOLD, true) \
_("pretenureGroupThreshold", JSGC_PRETENURE_GROUP_THRESHOLD, true) \
_("zoneAllocDelayKB", JSGC_ZONE_ALLOC_DELAY_KB, true) \
_("mallocThresholdBase", JSGC_MALLOC_THRESHOLD_BASE, true) \
_("urgentThreshold", JSGC_URGENT_THRESHOLD_MB, true) \
_("chunkBytes", JSGC_CHUNK_BYTES, false) \
_("helperThreadRatio", JSGC_HELPER_THREAD_RATIO, true) \
_("maxHelperThreads", JSGC_MAX_HELPER_THREADS, true) \
_("helperThreadCount", JSGC_HELPER_THREAD_COUNT, false) \
_("systemPageSizeKB", JSGC_SYSTEM_PAGE_SIZE_KB, false)
// Get the key and writability give a GC parameter name.
extern bool GetGCParameterInfo(const char* name, JSGCParamKey* keyOut,
bool* writableOut);
extern void TraceRuntime(JSTracer* trc);
// Trace roots but don't evict the nursery first; used from DumpHeap.

View File

@ -474,6 +474,33 @@ uint64_t js::gc::NextCellUniqueId(JSRuntime* rt) {
}
namespace js {
static const struct GCParamInfo {
const char* name;
JSGCParamKey key;
bool writable;
} GCParameters[] = {
#define DEFINE_PARAM_INFO(name, key, writable) {name, key, writable},
FOR_EACH_GC_PARAM(DEFINE_PARAM_INFO)
#undef DEFINE_PARAM_INFO
};
bool GetGCParameterInfo(const char* name, JSGCParamKey* keyOut,
bool* writableOut) {
MOZ_ASSERT(keyOut);
MOZ_ASSERT(writableOut);
for (const GCParamInfo& info : GCParameters) {
if (strcmp(name, info.name) == 0) {
*keyOut = info.key;
*writableOut = info.writable;
return true;
}
}
return false;
}
namespace gc {
namespace MemInfo {

View File

@ -11847,6 +11847,59 @@ static bool WriteSelfHostedXDRFile(JSContext* cx, JS::SelfHostedCache buffer) {
return true;
}
static bool SetGCParameterFromArg(JSContext* cx, char* arg) {
char* c = strchr(arg, '=');
if (!c) {
fprintf(stderr,
"Error: --gc-param argument '%s' must be of the form "
"name=decimalValue\n",
arg);
return false;
}
*c = '\0';
const char* name = arg;
const char* valueStr = c + 1;
JSGCParamKey key;
bool writable;
if (!GetGCParameterInfo(name, &key, &writable)) {
fprintf(stderr, "Error: Unknown GC parameter name '%s'\n", name);
fprintf(stderr, "Writable GC parameter names are:\n");
#define PRINT_WRITABLE_PARAM_NAME(name, _, writable) \
if (writable) { \
fprintf(stderr, " %s\n", name); \
}
FOR_EACH_GC_PARAM(PRINT_WRITABLE_PARAM_NAME)
#undef PRINT_WRITABLE_PARAM_NAME
return false;
}
if (!writable) {
fprintf(stderr, "Error: GC parameter '%s' is not writable\n", name);
return false;
}
char* end = nullptr;
unsigned long int value = strtoul(valueStr, &end, 10);
if (end == valueStr || *end) {
fprintf(stderr,
"Error: Could not parse '%s' as decimal for GC parameter '%s'\n",
valueStr, name);
return false;
}
uint32_t paramValue = uint32_t(value);
if (value == ULONG_MAX || value != paramValue ||
!cx->runtime()->gc.setParameter(key, paramValue)) {
fprintf(stderr, "Error: Value %s is out of range for GC parameter '%s'\n",
valueStr, name);
return false;
}
return true;
}
int main(int argc, char** argv) {
PreInit();
@ -12268,6 +12321,8 @@ int main(int argc, char** argv) {
!op.addStringOption('z', "gc-zeal", "LEVEL(;LEVEL)*[,N]",
"option ignored in non-gc-zeal builds") ||
#endif
!op.addMultiStringOption('\0', "gc-param", "NAME=VALUE",
"Set a named GC parameter") ||
!op.addStringOption('\0', "module-load-path", "DIR",
"Set directory to load modules from") ||
!op.addBoolOption('\0', "no-source-pragmas",
@ -12287,12 +12342,12 @@ int main(int argc, char** argv) {
!op.addBoolOption('\0', "smoosh", "No-op") ||
#endif
!op.addStringOption(
'\0', "delazification-mode", "[option]",
"Select one of the delazification mode for scripts given on the "
"command line, valid options are: "
"'on-demand', 'concurrent-df', 'eager', 'concurrent-df+on-demand'. "
"Choosing 'concurrent-df+on-demand' will run both concurrent-df and "
"on-demand delazification mode, and compare compilation outcome. ") ||
'\0', "delazification-mode", "[option]",
"Select one of the delazification mode for scripts given on the "
"command line, valid options are: "
"'on-demand', 'concurrent-df', 'eager', 'concurrent-df+on-demand'. "
"Choosing 'concurrent-df+on-demand' will run both concurrent-df and "
"on-demand delazification mode, and compare compilation outcome. ") ||
!op.addBoolOption('\0', "wasm-compile-and-serialize",
"Compile the wasm bytecode from stdin and serialize "
"the results to stdout") ||
@ -12679,6 +12734,13 @@ int main(int argc, char** argv) {
JS_SetGCParameter(cx, JSGC_PER_ZONE_GC_ENABLED, true);
for (MultiStringRange args = op.getMultiStringOption("gc-param");
!args.empty(); args.popFront()) {
if (!SetGCParameterFromArg(cx, args.front())) {
return EXIT_FAILURE;
}
}
JS::SetProcessLargeAllocationFailureCallback(my_LargeAllocFailCallback);
js::SetPreserveWrapperCallbacks(cx, DummyPreserveWrapperCallback,