summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authornineonine <mail4chemik@gmail.com>2022-12-15 16:55:20 -0800
committernineonine <mail4chemik@gmail.com>2022-12-15 16:55:20 -0800
commit4f46dffc3113f02a14103e45d6bd5db3315a8e69 (patch)
treef477a3c192e672dcd0e7c917a758ecb3181e20d0
parentc30accc2f8a0585c76cb534beda04fba624bce1c (diff)
downloadhaskell-4f46dffc3113f02a14103e45d6bd5db3315a8e69.tar.gz
WIP T19626 initial commit
-rw-r--r--TODO7
-rw-r--r--rts/OptParse.c357
-rw-r--r--rts/include/Rts.h1
-rw-r--r--rts/include/rts/Flags.h2
-rw-r--r--rts/include/rts/OptParse.h91
-rw-r--r--rts/rts.cabal.in2
-rw-r--r--testsuite/tests/rts/OptParseTest.c354
-rw-r--r--testsuite/tests/rts/OptParseTest.stderr411
-rw-r--r--testsuite/tests/rts/OptParseTest.stdout269
-rw-r--r--testsuite/tests/rts/all.T2
10 files changed, 1495 insertions, 1 deletions
diff --git a/TODO b/TODO
new file mode 100644
index 0000000000..c48d0c8959
--- /dev/null
+++ b/TODO
@@ -0,0 +1,7 @@
+update documentation (once all new names are approved)
+improved error: flag name suggestions
+improved error: flag value suggestions
+improved error: 'no value' error
+question about casting StgWord64 to StgWord
+perhaps move additional arithmetic manipulations out of `parseArg`
+print out flag 'tail' in some error messages
diff --git a/rts/OptParse.c b/rts/OptParse.c
new file mode 100644
index 0000000000..c4e4b9ab31
--- /dev/null
+++ b/rts/OptParse.c
@@ -0,0 +1,357 @@
+#include "Rts.h"
+#include "RtsUtils.h"
+
+#if defined(HAVE_CTYPE_H)
+#include <ctype.h>
+#endif
+#include <stdbool.h>
+#include <stdio.h>
+#include <string.h>
+
+#define FLAGS_LENGTH ((int)(sizeof(rtsFlags) / sizeof(rtsFlags[0])))
+#define SAFE true
+#define UNSAFE false
+
+#define MAX(X, Y) (((X) > (Y)) ? (X) : (Y))
+
+#define UNKNOWN_RTS_OPTION(error, arg) \
+ do { \
+ *error = true; \
+ errorBelch("unknown RTS option: %s" ,arg); \
+ return NO_VAL(UNKNOWN_RTS_OPTION); \
+ } while (false)
+
+#define BAD_VALUE(error, arg) \
+ do { \
+ *error = true; \
+ errorBelch("bad value for %s" ,arg); \
+ return NO_VAL(UNKNOWN_RTS_OPTION); \
+ } while (false)
+
+#define UNEXPECTED_ARGUMENT(error, name, arg) \
+ do { \
+ *error = true; \
+ errorBelch("flag %s given an argument when none was expected: %s" , name, arg); \
+ return NO_VAL(UNKNOWN_RTS_OPTION); \
+ } while (false)
+
+RtsFlagName
+rtsFlags[] = {
+ [HELP] = {SAFE, VOID, NULL, "?" , false},
+ [INSTALL_SIGNAL_HANDLERS] = {UNSAFE, BOOL, "install-signal-handlers", NULL, false},
+ [INSTALL_SEH_HANDLERS] = {UNSAFE, BOOL, "install-seh-handlers", NULL, false},
+ [GENERATE_STACK_TRACES] = {UNSAFE, BOOL, "generate-stack-traces", NULL, false},
+ [GENERATE_CRASH_DUMPS] = {UNSAFE, BOOL, "generate-crash-dumps", NULL, false},
+ [NULL_EVENTLOG_WRITER] = {UNSAFE, BOOL, "null-eventlog-writer", NULL, false},
+ [MACHINE_READABLE] = {UNSAFE, BOOL, "machine-readable", NULL, false},
+ [DISABLE_OS_MEM_RET] = {UNSAFE, BOOL, "disable-delayed-os-memory-return", NULL, false},
+ [INTERNAL_COUNTERS] = {SAFE, BOOL, "internal-counters", NULL, false},
+ [IO_MANAGER_FLAG] = {UNSAFE, ENUM, "io-manager", NULL, true},
+ [INFO] = {SAFE, VOID, "info", NULL, false},
+ [EVENTLOG_FLUSH_INTERVAL] = {SAFE, DOUBLE, "eventlog-flush-interval", NULL, true},
+ [COPYING_GC] = {SAFE, VOID, "copying-gc", NULL, false},
+ [NONMOVING_GC] = {SAFE, VOID, "nonmoving-gc", NULL, false},
+ [LARGE_OBJ_ALLOC_AREA] = {UNSAFE, STGWORD64, "large-object-allocation", "AL", true},
+ [MIN_ALLOC_AREA] = {UNSAFE, STGWORD64, "minimum-allocation-area-size", "A", true},
+// #if defined(THREADED_RTS)
+// #if defined(mingw32_HOST_OS)
+ [IO_MANAGER_THREADS] = {UNSAFE, STGWORD64, "io-manager-threads", NULL, true},
+// #endif
+ [NUMA] = {SAFE, STGWORD64, "numa", NULL, false},
+// #endif
+// #if defined(DEBUG) && defined(THREADED_RTS)
+ [DEBUG_NUMA] = {SAFE, STGWORD64, "debug-numa", NULL, true},
+// #endif
+ [LONG_GC_SYNC] = {SAFE, DOUBLE, "long-gc-sync", NULL, false},
+ [NO_AUTO_HEAP_SAMPLES] = {UNSAFE, BOOL, "no-automatic-heap-samples", NULL, false},
+ [NURSERY_CHUNK_SIZE] = {UNSAFE, STGWORD64, "alloc-area-chunksize", "n", true},
+ [GC_BELL] = {UNSAFE, VOID, "gc-bell", "B", false},
+ [COMPACT_GC] = {UNSAFE, DOUBLE, "compact-gc", "c", false},
+ // The 'NULL' of flags. Long name just for debugging
+ [UNKNOWN_RTS_OPTION] = {SAFE, VOID, "UNKNOWN_RTS_OPTION", NULL, false},
+};
+
+static RtsFlagValue
+parse_flag_value(RtsFlagKey i, bool isLongName, char *arg, bool *error);
+
+static double
+parseDouble(const char *arg, bool *error)
+{
+ char *endptr;
+ double out;
+ errno = 0;
+
+ out = strtod(arg, &endptr);
+
+ if (errno != 0 || endptr == arg) {
+ *error = true;
+ return out;
+ }
+
+ while (isspace((unsigned char)*endptr)) {
+ ++endptr;
+ }
+
+ if (*endptr != 0) {
+ *error = true;
+ }
+
+ return out;
+}
+
+
+static StgWord64
+decodeSize(const char *flag, uint32_t offset, StgWord64 min, StgWord64 max);
+
+static int
+find_first_numeric_char(const char *s) {
+ int len = strlen(s);
+ for (int i = 0; i < len; i++) {
+ if (isdigit(s[i])) {
+ return i;
+ }
+ }
+ return len;
+}
+
+static char*
+name_from_arg(bool longName, const char* str) {
+ ASSERT(str != NULL);
+ int i;
+ int str_len = strlen(str);
+ char* substring = stgMallocBytes(str_len + 1, "name_from_arg"); // allocate memory for the substring
+
+ strcpy(substring, str);
+
+ if (!longName) {
+ substring[find_first_numeric_char(str)] = '\0';
+ } else {
+ for (i = 0; i < str_len; i++) {
+ if (substring[i] == '=') {
+ substring[i] = '\0';
+ break;
+ }
+ }
+ }
+
+ return substring;
+}
+
+RtsFlagValue
+parseArg(char *arg, bool *error)
+{
+ // debugBelch("------- parseArg: %s\n", arg);
+ if (!strncmp("-?", arg, 2)) return NO_VAL(HELP);
+ for (int i = 0; i < FLAGS_LENGTH; i++) {
+ bool isLongName = arg[1] == '-';
+
+ RtsFlagName flag = rtsFlags[i];
+ if (isLongName && flag.longName == NULL) continue;
+ if (!isLongName && flag.shortName == NULL) continue;
+
+ char* name = isLongName ? flag.longName : flag.shortName;
+ if (name == NULL) continue;
+
+ char* arg1 = isLongName ? &arg[2] : &arg[1];
+ char* nameFromArg = name_from_arg(isLongName, arg1);
+ if (!strncmp(nameFromArg, name, MAX(strlen(nameFromArg), strlen(name)))) {
+ stgFree(nameFromArg);
+ return parse_flag_value(i, isLongName, arg, error);
+ }
+ stgFree(nameFromArg);
+ }
+ UNKNOWN_RTS_OPTION(error, arg);
+}
+
+static RtsFlagValue
+parse_flag_value(RtsFlagKey i, bool isLongName, char *arg0, bool *error)
+{
+ RtsFlagName flag = rtsFlags[i];
+ char* name = isLongName ? flag.longName : flag.shortName;
+ char *arg = isLongName ? &arg0[2] : &arg0[1];
+ // offset at which value can be found. Does not account for potential `=`
+ int offset = isLongName ? strlen(flag.longName) : strlen(flag.shortName);
+ bool isSwitch = !rtsFlags[i].valueRequired && arg[offset] == '\0';
+ bool hasValue = (isLongName && arg[offset] == '=' && arg[offset+1] != '\0')
+ || (!isLongName && arg[offset] != '\0');
+
+ // missing value - return immediately
+ if (!isSwitch && !hasValue) UNKNOWN_RTS_OPTION(error, arg0);
+ if (isSwitch && hasValue) UNKNOWN_RTS_OPTION(error, arg0);
+
+ switch (flag.valueType) {
+ case VOID: {
+ switch (i) {
+ case GC_BELL:
+ if (hasValue) UNEXPECTED_ARGUMENT(error, name, arg0);
+ }
+ return NO_VAL(i);
+ }
+ case BOOL: {
+ // we forbid having bool flags with '=' with no specified value
+ if (isSwitch) {
+ return BOOL_VAL(i, true);
+ } else {
+ if (!strncmp("yes", &arg[offset + 1], 3)) {
+ return BOOL_VAL(i, true);
+ } else if (!strncmp("no", &arg[offset + 1], 2)) {
+ return BOOL_VAL(i, false);
+ } else {
+ UNKNOWN_RTS_OPTION(error, arg0);
+ }
+ }
+ break;
+ }
+ case ENUM: {
+ switch (i) {
+ case IO_MANAGER_FLAG: {
+ if (!strncmp("native", &arg[offset + 1], 6)) {
+ return ENUM_VAL(i, IO_MNGR_NATIVE);
+ } else if (!strncmp("posix", &arg[offset + 1], 5)) {
+ return ENUM_VAL(i, IO_MNGR_POSIX);
+ }
+ }
+ default:
+ *error = true;
+ errorBelch("invalid enum '%s' for '%s'", &arg[offset + 1], rtsFlags[i].longName);
+ }
+ break;
+ }
+ case DOUBLE: {
+ double res;
+ switch (i) {
+ case EVENTLOG_FLUSH_INTERVAL: {
+ res = parseDouble(arg+offset+1, error);
+ break;
+ }
+ case LONG_GC_SYNC: {
+ res = parseDouble(arg+offset+1, error);
+ break;
+ }
+ case COMPACT_GC: {
+ // special treatment when used as a switch
+ if (!hasValue) return NO_VAL(i);
+ res = parseDouble(arg+offset+1, error);
+ break;
+ }
+ default: {
+ *error = true;
+ errorBelch("invalid double '%s' for '%s'", &arg[offset + 1], rtsFlags[i].longName);
+ }
+ }
+ if (*error) {
+ BAD_VALUE(error, arg);
+ }
+ return DOUBLE_VAL(i, res);
+ }
+ case STGWORD64: {
+ StgWord64 value = 0;
+ // account for '=' that is used with long-form names
+ // some long-from names can have no value though so account for that as well
+ if (isLongName && arg[offset] == '=') offset++;
+ switch (i) {
+ case LARGE_OBJ_ALLOC_AREA: {
+ value = decodeSize(arg, offset, 2*BLOCK_SIZE,
+ HS_INT_MAX) / BLOCK_SIZE;
+ break;
+ }
+ case MIN_ALLOC_AREA: {
+ // minimum two blocks in the nursery, so that we have one
+ // to grab for allocate().
+ value = decodeSize(arg, offset, 2*BLOCK_SIZE,
+ HS_INT_MAX) / BLOCK_SIZE;
+ break;
+ }
+// #if defined(THREADED_RTS)
+// #if defined(mingw32_HOST_OS)
+ case IO_MANAGER_THREADS: {
+ value = (StgWord64)strtol(arg + offset, (char **) NULL, 10);
+ break;
+ }
+// #endif
+ case NUMA: {
+ if (isSwitch) {
+ value = (StgWord64)~0;
+ } else if (hasValue) {
+ value = (StgWord64)strtol(arg + offset, (char **) NULL, 10);
+ } else {
+ *error = true;
+ errorBelch("invalid RTS option: %s", arg0);
+ }
+ break;
+ }
+// #endif
+// #if defined(DEBUG) && defined(THREADED_RTS)
+ case DEBUG_NUMA: {
+ if (isdigit(arg[offset]) && hasValue) {
+ value = (StgWord64)strtol(arg + offset, (char **) NULL, 10);
+ } else {
+ UNKNOWN_RTS_OPTION(error, arg0);
+ }
+ if (value > MAX_NUMA_NODES) {
+ *error = true;
+ errorBelch("%s: Too many NUMA nodes (max %d)",
+ rtsFlags[i].longName, MAX_NUMA_NODES);
+ }
+ break;
+ }
+// #endif
+ case NURSERY_CHUNK_SIZE: {
+ value = decodeSize(arg, offset, 2*BLOCK_SIZE, HS_INT_MAX)
+ / BLOCK_SIZE;
+ break;
+ }
+ }
+ return STGWORD64_VAL(i, value);
+ }
+ default: {
+ UNKNOWN_RTS_OPTION(error, arg0);
+ }
+ }
+}
+
+/* -----------------------------------------------------------------------------
+ * decodeSize: parse a string containing a size, like 300K or 1.2M
+-------------------------------------------------------------------------- */
+static StgWord64
+decodeSize(const char *flag, uint32_t offset, StgWord64 min, StgWord64 max)
+{
+ char c;
+ const char *s;
+ StgDouble m;
+ StgWord64 val;
+
+ s = flag + offset;
+ // debugBelch("------- decodeSize s: %s\n", s);
+
+ if (!*s)
+ {
+ m = 0;
+ }
+ else
+ {
+ m = atof(s);
+ c = s[strlen(s)-1];
+
+ if (c == 'g' || c == 'G')
+ m *= 1024*1024*1024;
+ else if (c == 'm' || c == 'M')
+ m *= 1024*1024;
+ else if (c == 'k' || c == 'K')
+ m *= 1024;
+ else if (c == 'w' || c == 'W')
+ m *= sizeof(W_);
+ }
+
+ val = (StgWord64)m;
+ // debugBelch("------- decodeSize m: %f\n", m);
+ if (m < 0 || val < min || val > max) {
+ // printf doesn't like 64-bit format specs on Windows
+ // apparently, so fall back to unsigned long.
+ errorBelch("error in RTS option %s: size outside allowed range (%" FMT_Word " - %" FMT_Word ")", flag, (W_)min, (W_)max);
+ stg_exit(EXIT_FAILURE);
+ }
+
+ // debugBelch("------- decodeSize val: %" FMT_Word64 "\n", val);
+ return val;
+}
diff --git a/rts/include/Rts.h b/rts/include/Rts.h
index 90d8e5b324..4a7fd348d2 100644
--- a/rts/include/Rts.h
+++ b/rts/include/Rts.h
@@ -281,6 +281,7 @@ void _warnFail(const char *filename, unsigned int linenum);
#include "rts/StaticPtrTable.h"
#include "rts/Libdw.h"
#include "rts/LibdwPool.h"
+#include "rts/OptParse.h"
/* Misc stuff without a home */
DLL_IMPORT_RTS extern char **prog_argv; /* so we can get at these from Haskell */
diff --git a/rts/include/rts/Flags.h b/rts/include/rts/Flags.h
index e33d97b17c..0184083845 100644
--- a/rts/include/rts/Flags.h
+++ b/rts/include/rts/Flags.h
@@ -2,7 +2,7 @@
*
* (c) The GHC Team, 1998-2009
*
- * Datatypes that holds the command-line flag settings.
+ * Datatypes that hold the command-line flag settings.
*
* Do not #include this file directly: #include "Rts.h" instead.
*
diff --git a/rts/include/rts/OptParse.h b/rts/include/rts/OptParse.h
new file mode 100644
index 0000000000..36c9660ca5
--- /dev/null
+++ b/rts/include/rts/OptParse.h
@@ -0,0 +1,91 @@
+/* -----------------------------------------------------------------------------
+ *
+ * (c) The GHC Team, 1995-2023
+ *
+ * Interface to the RTS's flag parser
+ *
+ * Do not #include this file directly: #include "Rts.h" instead.
+ *
+ * To understand the structure of the RTS headers, see the wiki:
+ * https://gitlab.haskell.org/ghc/ghc/wikis/commentary/source-tree/includes
+ *
+ * ---------------------------------------------------------------------------*/
+
+#pragma once
+
+#include <stdbool.h>
+#include "stg/Types.h"
+
+// order is important - do not sort
+typedef enum _RtsFlagKey {
+ HELP,
+ INSTALL_SIGNAL_HANDLERS,
+ INSTALL_SEH_HANDLERS,
+ GENERATE_STACK_TRACES,
+ GENERATE_CRASH_DUMPS,
+ NULL_EVENTLOG_WRITER,
+ MACHINE_READABLE,
+ DISABLE_OS_MEM_RET,
+ INTERNAL_COUNTERS,
+ IO_MANAGER_FLAG,
+ INFO,
+ EVENTLOG_FLUSH_INTERVAL,
+ COPYING_GC,
+ NONMOVING_GC,
+ LARGE_OBJ_ALLOC_AREA,
+ MIN_ALLOC_AREA,
+ GC_BELL,
+// #if defined(THREADED_RTS)
+// #if defined(mingw32_HOST_OS)
+ IO_MANAGER_THREADS,
+// #endif
+ NUMA,
+// #endif
+// #if defined(DEBUG) && defined(THREADED_RTS)
+ DEBUG_NUMA,
+// #endif
+ LONG_GC_SYNC,
+ NO_AUTO_HEAP_SAMPLES,
+ NURSERY_CHUNK_SIZE,
+ COMPACT_GC,
+
+ UNKNOWN_RTS_OPTION,
+} RtsFlagKey;
+
+typedef enum _RtsFlagValueType {
+ VOID,
+ BOOL,
+ ENUM,
+ DOUBLE,
+ STGWORD64,
+} RtsFlagValueType;
+
+typedef struct _RtsFlagName {
+ bool optionSafe;
+ RtsFlagValueType valueType;
+ char* longName;
+ char* shortName;
+ bool valueRequired;
+} RtsFlagName;
+
+typedef struct _FlagValue {
+ RtsFlagKey key;
+ union {
+ bool boolean;
+ double _double;
+ int _enum;
+ char* text;
+ StgWord64 stgWord64;
+ } as;
+} RtsFlagValue;
+
+#define NO_VAL(flagKey) ((RtsFlagValue){flagKey, {.text = "VOID"}})
+#define BOOL_VAL(flagKey, value) ((RtsFlagValue){flagKey, {.boolean = value}})
+#define ENUM_VAL(flagKey, value) ((RtsFlagValue){flagKey, {._enum = value}})
+#define DOUBLE_VAL(flagKey, value) ((RtsFlagValue){flagKey, {._double = value}})
+#define STGWORD64_VAL(flagKey, value) ((RtsFlagValue){flagKey, {.stgWord64 = value}})
+
+#define IS_VOID(flagKey) ()
+
+extern RtsFlagName rtsFlags[];
+RtsFlagValue parseArg(char *arg, bool *error);
diff --git a/rts/rts.cabal.in b/rts/rts.cabal.in
index e4a78a64cf..ed4e85190b 100644
--- a/rts/rts.cabal.in
+++ b/rts/rts.cabal.in
@@ -227,6 +227,7 @@ library
rts/EventLogWriter.h
rts/FileLock.h
rts/Flags.h
+ rts/OptParse.h
rts/ForeignExports.h
rts/GetTime.h
rts/Globals.h
@@ -547,6 +548,7 @@ library
ReportMemoryMap.c
Messages.c
OldARMAtomic.c
+ OptParse.c
PathUtils.c
Pool.c
Printer.c
diff --git a/testsuite/tests/rts/OptParseTest.c b/testsuite/tests/rts/OptParseTest.c
new file mode 100644
index 0000000000..39fe2cb72d
--- /dev/null
+++ b/testsuite/tests/rts/OptParseTest.c
@@ -0,0 +1,354 @@
+#include <stdio.h>
+#include "Rts.h"
+
+#define SAFE true
+#define UNSAFE false
+
+static bool ERROR = false;
+
+static void _TEST( char* flagToTest
+ , int expectedFlagKey
+ , char* expectedLongName
+ , char* expectedShortName
+ , RtsFlagValueType expectedFlagValueType
+ , bool safe
+ , RtsFlagValue expectedValue
+ ) {
+ debugBelch("\n(TEST) input: %s\n", flagToTest);
+ printf("\n(TEST) input: %s\n", flagToTest);
+ RtsFlagValue flagValue = parseArg(flagToTest, &ERROR);
+ CHECK(!ERROR);
+ RtsFlagName flag = rtsFlags[flagValue.key];
+
+ printf("%i: %s %s %s\n", flagValue.key , flag.longName, flag.shortName, safe ? "SAFE": "UNSAFE");
+ debugBelch("%i: %s %s %s\n", flagValue.key , flag.longName, flag.shortName, safe ? "SAFE": "UNSAFE");
+ CHECK(flagValue.key == expectedFlagKey);
+ CHECK(flag.longName == expectedLongName);
+ CHECK(flag.shortName == expectedShortName);
+ CHECK(flag.valueType == expectedFlagValueType);
+ CHECK(flag.optionSafe == safe);
+ RtsFlagValueType valueTy = flag.valueType;
+ if (valueTy == BOOL) {
+ CHECK(expectedValue.as.boolean == flagValue.as.boolean);
+ printf("\tvalue: %s\n", flagValue.as.boolean ? "true" : "false");
+ }
+ if (valueTy == ENUM) {
+ CHECK(expectedValue.as._enum == flagValue.as._enum);
+ printf("\tvalue: %i\n", flagValue.as._enum);
+ }
+ if (valueTy == DOUBLE) {
+ debugBelch("expected: %f actual: %f\n", expectedValue.as._double, flagValue.as._double);
+ CHECK(expectedValue.as._double == flagValue.as._double);
+ printf("\tvalue: %f\n", flagValue.as._double);
+ }
+ if (valueTy == STGWORD64) {
+ debugBelch("expected: %" FMT_Word64 " actual: %" FMT_Word64 "\n", expectedValue.as.stgWord64, flagValue.as.stgWord64);
+ printf("\tvalue: %" FMT_Word64 "\n", flagValue.as.stgWord64);
+ // CHECK(expectedValue.as.stgWord64 == flagValue.as.stgWord64);
+ }
+}
+
+static void _FAIL_TEST(char* flagToTest) {
+ debugBelch("\n(FAIL_TEST) input: %s\n", flagToTest);
+ RtsFlagValue flagValue = parseArg(flagToTest, &ERROR);
+ CHECK(ERROR);
+ ERROR = false;
+}
+
+int main (int argc, char *argv[])
+{
+
+ printf("=== OptParseTest START ===\n");
+
+ _TEST( "-?", HELP
+ , NULL, "?"
+ , VOID, SAFE, NO_VAL(HELP));
+ // _FAIL_TEST("-?asfg");
+
+ _TEST( "--install-signal-handlers", INSTALL_SIGNAL_HANDLERS
+ , "install-signal-handlers", NULL
+ , BOOL, UNSAFE, BOOL_VAL(INSTALL_SIGNAL_HANDLERS, true));
+ _TEST( "--install-signal-handlers=yes", INSTALL_SIGNAL_HANDLERS
+ , "install-signal-handlers", NULL
+ , BOOL, UNSAFE, BOOL_VAL(INSTALL_SIGNAL_HANDLERS, true));
+ _TEST( "--install-signal-handlers=no", INSTALL_SIGNAL_HANDLERS
+ , "install-signal-handlers", NULL
+ , BOOL, UNSAFE, BOOL_VAL(INSTALL_SIGNAL_HANDLERS, false));
+ _FAIL_TEST("--install-signal-handlers=dunnow");
+ _FAIL_TEST("--install-signal-handlersgasg");
+
+ _TEST( "--install-seh-handlers", INSTALL_SEH_HANDLERS
+ , "install-seh-handlers", NULL
+ , BOOL, UNSAFE, BOOL_VAL(INSTALL_SEH_HANDLERS, true));
+ _TEST( "--install-seh-handlers=yes", INSTALL_SEH_HANDLERS
+ , "install-seh-handlers", NULL
+ , BOOL, UNSAFE, BOOL_VAL(INSTALL_SEH_HANDLERS, true));
+ _TEST( "--install-seh-handlers=no", INSTALL_SEH_HANDLERS
+ , "install-seh-handlers", NULL
+ , BOOL, UNSAFE, BOOL_VAL(INSTALL_SEH_HANDLERS, false));
+ _FAIL_TEST("--install-seh-handlers=");
+ _FAIL_TEST("--install-seh-handlers=hmmm");
+ _FAIL_TEST("--install-seh-handlersgasdxxxasg");
+
+ _TEST( "--generate-stack-traces", GENERATE_STACK_TRACES
+ , "generate-stack-traces", NULL
+ , BOOL, UNSAFE, BOOL_VAL(GENERATE_STACK_TRACES, true));
+ _TEST( "--generate-stack-traces=yes", GENERATE_STACK_TRACES
+ , "generate-stack-traces", NULL
+ , BOOL, UNSAFE, BOOL_VAL(GENERATE_STACK_TRACES, true));
+ _TEST( "--generate-stack-traces=no", GENERATE_STACK_TRACES
+ , "generate-stack-traces", NULL
+ , BOOL, UNSAFE, BOOL_VAL(GENERATE_STACK_TRACES, false));
+ _FAIL_TEST("--generate-stack-traces=perhaps");
+ _FAIL_TEST("--generate-stack-tracesgasg");
+
+ _TEST( "--generate-crash-dumps", GENERATE_CRASH_DUMPS
+ , "generate-crash-dumps", NULL
+ , BOOL, UNSAFE, BOOL_VAL(GENERATE_CRASH_DUMPS, true));
+ _TEST( "--generate-crash-dumps=yes", GENERATE_CRASH_DUMPS
+ , "generate-crash-dumps", NULL
+ , BOOL, UNSAFE, BOOL_VAL(GENERATE_CRASH_DUMPS, true));
+ _TEST( "--generate-crash-dumps=no", GENERATE_CRASH_DUMPS
+ , "generate-crash-dumps", NULL
+ , BOOL, UNSAFE, BOOL_VAL(GENERATE_CRASH_DUMPS, false));
+ _FAIL_TEST("--generate-crash-dumps=maybe");
+ _FAIL_TEST("--generate-crash-dumpssss");
+
+ _TEST( "--null-eventlog-writer", NULL_EVENTLOG_WRITER
+ , "null-eventlog-writer", NULL
+ , BOOL, UNSAFE, BOOL_VAL(NULL_EVENTLOG_WRITER, true));
+ _TEST( "--null-eventlog-writer=yes", NULL_EVENTLOG_WRITER
+ , "null-eventlog-writer", NULL
+ , BOOL, UNSAFE, BOOL_VAL(NULL_EVENTLOG_WRITER, true));
+ _TEST( "--null-eventlog-writer=no", NULL_EVENTLOG_WRITER
+ , "null-eventlog-writer", NULL
+ , BOOL, UNSAFE, BOOL_VAL(NULL_EVENTLOG_WRITER, false));
+ _FAIL_TEST("--null-eventlog-writer=");
+ _FAIL_TEST("--null-eventlog-writerytrwe");
+
+ _TEST( "--machine-readable", MACHINE_READABLE
+ , "machine-readable", NULL
+ , BOOL, UNSAFE, BOOL_VAL(MACHINE_READABLE, true));
+ _TEST( "--machine-readable=yes", MACHINE_READABLE
+ , "machine-readable", NULL
+ , BOOL, UNSAFE, BOOL_VAL(MACHINE_READABLE, true));
+ _TEST( "--machine-readable=no", MACHINE_READABLE
+ , "machine-readable", NULL
+ , BOOL, UNSAFE, BOOL_VAL(MACHINE_READABLE, false));
+ _FAIL_TEST("--machine-readable=treu");
+ _FAIL_TEST("--machine-readableytrweasf");
+
+ _TEST( "--disable-delayed-os-memory-return", DISABLE_OS_MEM_RET
+ , "disable-delayed-os-memory-return", NULL
+ , BOOL, UNSAFE, BOOL_VAL(DISABLE_OS_MEM_RET, true));
+ _TEST( "--disable-delayed-os-memory-return=yes", DISABLE_OS_MEM_RET
+ , "disable-delayed-os-memory-return", NULL
+ , BOOL, UNSAFE, BOOL_VAL(DISABLE_OS_MEM_RET, true));
+ _TEST( "--disable-delayed-os-memory-return=no", DISABLE_OS_MEM_RET
+ , "disable-delayed-os-memory-return", NULL
+ , BOOL, UNSAFE, BOOL_VAL(DISABLE_OS_MEM_RET, false));
+ _FAIL_TEST("--disable-delayed-os-memory-return=flase");
+ _FAIL_TEST("--disable-delayed-os-memory-returnysaftrweasf");
+
+ _TEST( "--internal-counters", INTERNAL_COUNTERS
+ , "internal-counters", NULL
+ , BOOL, SAFE, BOOL_VAL(INTERNAL_COUNTERS, true));
+ _TEST( "--internal-counters=yes", INTERNAL_COUNTERS
+ , "internal-counters", NULL
+ , BOOL, SAFE, BOOL_VAL(INTERNAL_COUNTERS, true));
+ _TEST( "--internal-counters=no", INTERNAL_COUNTERS
+ , "internal-counters", NULL
+ , BOOL, SAFE, BOOL_VAL(INTERNAL_COUNTERS, false));
+ _FAIL_TEST("--internal-counters=tutr");
+ _FAIL_TEST("--internal-countersysaftrweasfasf");
+
+ _TEST( "--io-manager=native", IO_MANAGER_FLAG
+ , "io-manager", NULL
+ , ENUM, UNSAFE, ENUM_VAL(IO_MANAGER_FLAG, IO_MNGR_NATIVE));
+ _TEST( "--io-manager=posix", IO_MANAGER_FLAG
+ , "io-manager", NULL
+ , ENUM, UNSAFE, ENUM_VAL(IO_MANAGER_FLAG, IO_MNGR_POSIX));
+ _FAIL_TEST("--io-manager");
+ _FAIL_TEST("--io-manager=");
+ _FAIL_TEST("--io-manager=unknown-manager");
+ _FAIL_TEST("--io-managerlgaks");
+
+ _TEST( "--info", INFO
+ , "info", NULL
+ , VOID, SAFE, NO_VAL(INFO));
+
+ _TEST( "--eventlog-flush-interval=606.909", EVENTLOG_FLUSH_INTERVAL
+ , "eventlog-flush-interval", NULL
+ , DOUBLE, SAFE, DOUBLE_VAL(EVENTLOG_FLUSH_INTERVAL, 606.909));
+ _TEST( "--eventlog-flush-interval=0.125", EVENTLOG_FLUSH_INTERVAL
+ , "eventlog-flush-interval", NULL
+ , DOUBLE, SAFE, DOUBLE_VAL(EVENTLOG_FLUSH_INTERVAL, 0.125));
+ _FAIL_TEST("--eventlog-flush-interval");
+ _FAIL_TEST("--eventlog-flush-interval=");
+ _FAIL_TEST("--eventlog-flush-interval=true");
+ _FAIL_TEST("--eventlog-flush-intervalysaftrweasfasf");
+
+ _TEST( "--copying-gc", COPYING_GC
+ , "copying-gc", NULL
+ , VOID, SAFE, NO_VAL(COPYING_GC));
+
+ _TEST( "--nonmoving-gc", NONMOVING_GC
+ , "nonmoving-gc", NULL
+ , VOID, SAFE, NO_VAL(NONMOVING_GC));
+
+ _TEST( "--large-object-allocation=8193K", LARGE_OBJ_ALLOC_AREA
+ , "large-object-allocation", "AL"
+ , STGWORD64, UNSAFE, STGWORD64_VAL(LARGE_OBJ_ALLOC_AREA, 8389632 / BLOCK_SIZE));
+ _TEST( "--large-object-allocation=2M", LARGE_OBJ_ALLOC_AREA
+ , "large-object-allocation", "AL"
+ , STGWORD64, UNSAFE, STGWORD64_VAL(LARGE_OBJ_ALLOC_AREA, 2097152 / BLOCK_SIZE));
+ _TEST( "-AL9G", LARGE_OBJ_ALLOC_AREA
+ , "large-object-allocation", "AL"
+ , STGWORD64, UNSAFE, STGWORD64_VAL(LARGE_OBJ_ALLOC_AREA, 9663676416 / BLOCK_SIZE));
+ _TEST( "-AL0.125G", LARGE_OBJ_ALLOC_AREA
+ , "large-object-allocation", "AL"
+ , STGWORD64, UNSAFE, STGWORD64_VAL(LARGE_OBJ_ALLOC_AREA, 134217728 / BLOCK_SIZE));
+ _TEST( "-AL3333w", LARGE_OBJ_ALLOC_AREA
+ , "large-object-allocation", "AL"
+ , STGWORD64, UNSAFE, STGWORD64_VAL(LARGE_OBJ_ALLOC_AREA, 26664 / BLOCK_SIZE));
+ _FAIL_TEST("-AL");
+ _FAIL_TEST("--large-object-allocation");
+ _FAIL_TEST("--large-object-allocation=");
+
+ _TEST( "--minimum-allocation-area-size=8193K", MIN_ALLOC_AREA
+ , "minimum-allocation-area-size", "A"
+ , STGWORD64, UNSAFE, STGWORD64_VAL(MIN_ALLOC_AREA, 8389632 / BLOCK_SIZE));
+ _TEST( "--minimum-allocation-area-size=2M", MIN_ALLOC_AREA
+ , "minimum-allocation-area-size", "A"
+ , STGWORD64, UNSAFE, STGWORD64_VAL(MIN_ALLOC_AREA, 2097152 / BLOCK_SIZE));
+ _TEST( "-A9G", MIN_ALLOC_AREA
+ , "minimum-allocation-area-size", "A"
+ , STGWORD64, UNSAFE, STGWORD64_VAL(MIN_ALLOC_AREA, 9663676416 / BLOCK_SIZE));
+ _TEST( "-A0.125G", MIN_ALLOC_AREA
+ , "minimum-allocation-area-size", "A"
+ , STGWORD64, UNSAFE, STGWORD64_VAL(MIN_ALLOC_AREA, 134217728 / BLOCK_SIZE));
+ _TEST( "-A3333w", MIN_ALLOC_AREA
+ , "minimum-allocation-area-size", "A"
+ , STGWORD64, UNSAFE, STGWORD64_VAL(MIN_ALLOC_AREA, 26664 / BLOCK_SIZE));
+ _FAIL_TEST("-A");
+ _FAIL_TEST("--minimum-allocation-area-size");
+ _FAIL_TEST("--minimum-allocation-area-size=");
+
+ _TEST( "--io-manager-threads=1", IO_MANAGER_THREADS
+ , "io-manager-threads", NULL
+ , STGWORD64, UNSAFE, STGWORD64_VAL(IO_MANAGER_THREADS, 1));
+ _TEST( "--io-manager-threads=1234567", IO_MANAGER_THREADS
+ , "io-manager-threads", NULL
+ , STGWORD64, UNSAFE, STGWORD64_VAL(IO_MANAGER_THREADS, 1234567));
+ _FAIL_TEST("--io-manager-threads");
+ _FAIL_TEST("--io-manager-threads=");
+
+ _TEST( "--numa", NUMA
+ , "numa", NULL
+ , STGWORD64, SAFE, STGWORD64_VAL(NUMA, (StgWord)~0));
+ _TEST( "--numa=1", NUMA
+ , "numa", NULL
+ , STGWORD64, SAFE, STGWORD64_VAL(NUMA, 1));
+ _TEST( "--numa=1234567", NUMA
+ , "numa", NULL
+ , STGWORD64, SAFE, STGWORD64_VAL(NUMA, 1234567));
+ _FAIL_TEST("--numa=");
+
+ _TEST( "--debug-numa=1", DEBUG_NUMA
+ , "debug-numa", NULL
+ , STGWORD64, SAFE, STGWORD64_VAL(DEBUG_NUMA, 1));
+ _TEST( "--debug-numa=8", DEBUG_NUMA
+ , "debug-numa", NULL
+ , STGWORD64, SAFE, STGWORD64_VAL(DEBUG_NUMA, 8));
+ _FAIL_TEST("--debug-numa=999");
+ _FAIL_TEST("--debug-numa999");
+ _FAIL_TEST("--debug-numa=");
+ _FAIL_TEST("--debug-numa");
+ _FAIL_TEST("--debug-num");
+
+ _TEST( "--long-gc-sync=606.909", LONG_GC_SYNC
+ , "long-gc-sync", NULL
+ , DOUBLE, SAFE, DOUBLE_VAL(LONG_GC_SYNC, 606.909));
+ _TEST( "--long-gc-sync=0.125", LONG_GC_SYNC
+ , "long-gc-sync", NULL
+ , DOUBLE, SAFE, DOUBLE_VAL(LONG_GC_SYNC, 0.125));
+ _FAIL_TEST("--long-gc-sync"); // this is now failure. previously it was a no-op ... ?
+ _FAIL_TEST("--long-gc-sync=");
+ _FAIL_TEST("--long-gc-sync=true");
+ _FAIL_TEST("--long-gc-syncysaftrweasfasf");
+
+ _TEST( "--no-automatic-heap-samples", NO_AUTO_HEAP_SAMPLES
+ , "no-automatic-heap-samples", NULL
+ , BOOL, UNSAFE, BOOL_VAL(NO_AUTO_HEAP_SAMPLES, true));
+ _TEST( "--no-automatic-heap-samples=yes", NO_AUTO_HEAP_SAMPLES
+ , "no-automatic-heap-samples", NULL
+ , BOOL, UNSAFE, BOOL_VAL(NO_AUTO_HEAP_SAMPLES, true));
+ _TEST( "--no-automatic-heap-samples=no", NO_AUTO_HEAP_SAMPLES
+ , "no-automatic-heap-samples", NULL
+ , BOOL, UNSAFE, BOOL_VAL(NO_AUTO_HEAP_SAMPLES, false));
+ _FAIL_TEST("--no-automatic-heap-samples=");
+ _FAIL_TEST("--no-automatic-heap-samples=foo");
+ _FAIL_TEST("--no-automatic-heap-samplasfsg");
+
+ _TEST( "--alloc-area-chunksize=16M", NURSERY_CHUNK_SIZE
+ , "alloc-area-chunksize", "n"
+ , STGWORD64, UNSAFE, STGWORD64_VAL(NURSERY_CHUNK_SIZE, 4096));
+ _TEST( "-n16m", NURSERY_CHUNK_SIZE
+ , "alloc-area-chunksize", "n"
+ , STGWORD64, UNSAFE, STGWORD64_VAL(NURSERY_CHUNK_SIZE, 4096));
+ _TEST( "--alloc-area-chunksize=1234567", NURSERY_CHUNK_SIZE
+ , "alloc-area-chunksize", "n"
+ , STGWORD64, UNSAFE, STGWORD64_VAL(NURSERY_CHUNK_SIZE, 301));
+ _TEST( "-n1239999", NURSERY_CHUNK_SIZE
+ , "alloc-area-chunksize", "n"
+ , STGWORD64, UNSAFE, STGWORD64_VAL(NURSERY_CHUNK_SIZE, 302));
+ _TEST( "--alloc-area-chunksize=0.225G", NURSERY_CHUNK_SIZE
+ , "alloc-area-chunksize", "n"
+ , STGWORD64, UNSAFE, STGWORD64_VAL(NURSERY_CHUNK_SIZE, 58982));
+ _TEST( "-n99999999k", NURSERY_CHUNK_SIZE
+ , "alloc-area-chunksize", "n"
+ , STGWORD64, UNSAFE, STGWORD64_VAL(NURSERY_CHUNK_SIZE, 24999999));
+ _TEST( "--alloc-area-chunksize=7654W", NURSERY_CHUNK_SIZE
+ , "alloc-area-chunksize", "n"
+ , STGWORD64, UNSAFE, STGWORD64_VAL(NURSERY_CHUNK_SIZE, 14));
+ _TEST( "-n4567w", NURSERY_CHUNK_SIZE
+ , "alloc-area-chunksize", "n"
+ , STGWORD64, UNSAFE, STGWORD64_VAL(NURSERY_CHUNK_SIZE, 8));
+ _FAIL_TEST("--alloc-area-chunksize");
+ _FAIL_TEST("--alloc-area-chunksizr");
+ _FAIL_TEST("--alloc-area-chunksizr=23M");
+ _FAIL_TEST("--alloc-area-chunksize=");
+// _FAIL_TEST("--alloc-area-chunksize=0");
+ _FAIL_TEST("-n");
+ _FAIL_TEST("-n=");
+// _FAIL_TEST("-n0");
+ _FAIL_TEST("-n=0");
+
+ _TEST( "-B", GC_BELL
+ , "gc-bell", "B"
+ , VOID, UNSAFE, NO_VAL(GC_BELL));
+ _TEST( "--gc-bell", GC_BELL
+ , "gc-bell", "B"
+ , VOID, UNSAFE, NO_VAL(GC_BELL));
+ _FAIL_TEST("--gc-bell=");
+ _FAIL_TEST("--gc-bell=123G");
+ _FAIL_TEST("-B123G");
+
+ _TEST( "--compact-gc", COMPACT_GC
+ , "compact-gc", "c"
+ , DOUBLE, UNSAFE, NO_VAL(COMPACT_GC));
+ _TEST( "-c", COMPACT_GC
+ , "compact-gc", "c"
+ , DOUBLE, UNSAFE, NO_VAL(COMPACT_GC));
+ _TEST( "--compact-gc=1125", COMPACT_GC
+ , "compact-gc", "c"
+ , DOUBLE, UNSAFE, DOUBLE_VAL(COMPACT_GC, 1125.0));
+ _TEST( "-c", COMPACT_GC
+ , "compact-gc", "c"
+ , DOUBLE, UNSAFE, NO_VAL(COMPACT_GC));
+ _FAIL_TEST("--compact-gc=");
+ _FAIL_TEST("--compact-gc=blah");
+
+ printf("\n=== OptParseTest END ===\n");
+ return 0;
+}
diff --git a/testsuite/tests/rts/OptParseTest.stderr b/testsuite/tests/rts/OptParseTest.stderr
new file mode 100644
index 0000000000..6c996ec6fb
--- /dev/null
+++ b/testsuite/tests/rts/OptParseTest.stderr
@@ -0,0 +1,411 @@
+
+(TEST) input: -?
+0: (null) ? SAFE
+
+(TEST) input: --install-signal-handlers
+1: install-signal-handlers (null) UNSAFE
+
+(TEST) input: --install-signal-handlers=yes
+1: install-signal-handlers (null) UNSAFE
+
+(TEST) input: --install-signal-handlers=no
+1: install-signal-handlers (null) UNSAFE
+
+(FAIL_TEST) input: --install-signal-handlers=dunnow
+unknown RTS option: --install-signal-handlers=dunnow
+
+(FAIL_TEST) input: --install-signal-handlersgasg
+unknown RTS option: --install-signal-handlersgasg
+
+(TEST) input: --install-seh-handlers
+2: install-seh-handlers (null) UNSAFE
+
+(TEST) input: --install-seh-handlers=yes
+2: install-seh-handlers (null) UNSAFE
+
+(TEST) input: --install-seh-handlers=no
+2: install-seh-handlers (null) UNSAFE
+
+(FAIL_TEST) input: --install-seh-handlers=
+unknown RTS option: --install-seh-handlers=
+
+(FAIL_TEST) input: --install-seh-handlers=hmmm
+unknown RTS option: --install-seh-handlers=hmmm
+
+(FAIL_TEST) input: --install-seh-handlersgasdxxxasg
+unknown RTS option: --install-seh-handlersgasdxxxasg
+
+(TEST) input: --generate-stack-traces
+3: generate-stack-traces (null) UNSAFE
+
+(TEST) input: --generate-stack-traces=yes
+3: generate-stack-traces (null) UNSAFE
+
+(TEST) input: --generate-stack-traces=no
+3: generate-stack-traces (null) UNSAFE
+
+(FAIL_TEST) input: --generate-stack-traces=perhaps
+unknown RTS option: --generate-stack-traces=perhaps
+
+(FAIL_TEST) input: --generate-stack-tracesgasg
+unknown RTS option: --generate-stack-tracesgasg
+
+(TEST) input: --generate-crash-dumps
+4: generate-crash-dumps (null) UNSAFE
+
+(TEST) input: --generate-crash-dumps=yes
+4: generate-crash-dumps (null) UNSAFE
+
+(TEST) input: --generate-crash-dumps=no
+4: generate-crash-dumps (null) UNSAFE
+
+(FAIL_TEST) input: --generate-crash-dumps=maybe
+unknown RTS option: --generate-crash-dumps=maybe
+
+(FAIL_TEST) input: --generate-crash-dumpssss
+unknown RTS option: --generate-crash-dumpssss
+
+(TEST) input: --null-eventlog-writer
+5: null-eventlog-writer (null) UNSAFE
+
+(TEST) input: --null-eventlog-writer=yes
+5: null-eventlog-writer (null) UNSAFE
+
+(TEST) input: --null-eventlog-writer=no
+5: null-eventlog-writer (null) UNSAFE
+
+(FAIL_TEST) input: --null-eventlog-writer=
+unknown RTS option: --null-eventlog-writer=
+
+(FAIL_TEST) input: --null-eventlog-writerytrwe
+unknown RTS option: --null-eventlog-writerytrwe
+
+(TEST) input: --machine-readable
+6: machine-readable (null) UNSAFE
+
+(TEST) input: --machine-readable=yes
+6: machine-readable (null) UNSAFE
+
+(TEST) input: --machine-readable=no
+6: machine-readable (null) UNSAFE
+
+(FAIL_TEST) input: --machine-readable=treu
+unknown RTS option: --machine-readable=treu
+
+(FAIL_TEST) input: --machine-readableytrweasf
+unknown RTS option: --machine-readableytrweasf
+
+(TEST) input: --disable-delayed-os-memory-return
+7: disable-delayed-os-memory-return (null) UNSAFE
+
+(TEST) input: --disable-delayed-os-memory-return=yes
+7: disable-delayed-os-memory-return (null) UNSAFE
+
+(TEST) input: --disable-delayed-os-memory-return=no
+7: disable-delayed-os-memory-return (null) UNSAFE
+
+(FAIL_TEST) input: --disable-delayed-os-memory-return=flase
+unknown RTS option: --disable-delayed-os-memory-return=flase
+
+(FAIL_TEST) input: --disable-delayed-os-memory-returnysaftrweasf
+unknown RTS option: --disable-delayed-os-memory-returnysaftrweasf
+
+(TEST) input: --internal-counters
+8: internal-counters (null) SAFE
+
+(TEST) input: --internal-counters=yes
+8: internal-counters (null) SAFE
+
+(TEST) input: --internal-counters=no
+8: internal-counters (null) SAFE
+
+(FAIL_TEST) input: --internal-counters=tutr
+unknown RTS option: --internal-counters=tutr
+
+(FAIL_TEST) input: --internal-countersysaftrweasfasf
+unknown RTS option: --internal-countersysaftrweasfasf
+
+(TEST) input: --io-manager=native
+9: io-manager (null) UNSAFE
+
+(TEST) input: --io-manager=posix
+9: io-manager (null) UNSAFE
+
+(FAIL_TEST) input: --io-manager
+unknown RTS option: --io-manager
+
+(FAIL_TEST) input: --io-manager=
+unknown RTS option: --io-manager=
+
+(FAIL_TEST) input: --io-manager=unknown-manager
+invalid enum 'unknown-manager' for 'io-manager'
+
+(FAIL_TEST) input: --io-managerlgaks
+unknown RTS option: --io-managerlgaks
+
+(TEST) input: --info
+10: info (null) SAFE
+
+(TEST) input: --eventlog-flush-interval=606.909
+11: eventlog-flush-interval (null) SAFE
+expected: 606.909000 actual: 606.909000
+
+(TEST) input: --eventlog-flush-interval=0.125
+11: eventlog-flush-interval (null) SAFE
+expected: 0.125000 actual: 0.125000
+
+(FAIL_TEST) input: --eventlog-flush-interval
+unknown RTS option: --eventlog-flush-interval
+
+(FAIL_TEST) input: --eventlog-flush-interval=
+unknown RTS option: --eventlog-flush-interval=
+
+(FAIL_TEST) input: --eventlog-flush-interval=true
+bad value for eventlog-flush-interval=true
+
+(FAIL_TEST) input: --eventlog-flush-intervalysaftrweasfasf
+unknown RTS option: --eventlog-flush-intervalysaftrweasfasf
+
+(TEST) input: --copying-gc
+12: copying-gc (null) SAFE
+
+(TEST) input: --nonmoving-gc
+13: nonmoving-gc (null) SAFE
+
+(TEST) input: --large-object-allocation=8193K
+14: large-object-allocation AL UNSAFE
+expected: 2048 actual: 2048
+
+(TEST) input: --large-object-allocation=2M
+14: large-object-allocation AL UNSAFE
+expected: 512 actual: 512
+
+(TEST) input: -AL9G
+14: large-object-allocation AL UNSAFE
+expected: 2359296 actual: 2359296
+
+(TEST) input: -AL0.125G
+14: large-object-allocation AL UNSAFE
+expected: 32768 actual: 32768
+
+(TEST) input: -AL3333w
+14: large-object-allocation AL UNSAFE
+expected: 6 actual: 6
+
+(FAIL_TEST) input: -AL
+unknown RTS option: -AL
+
+(FAIL_TEST) input: --large-object-allocation
+unknown RTS option: --large-object-allocation
+
+(FAIL_TEST) input: --large-object-allocation=
+unknown RTS option: --large-object-allocation=
+
+(TEST) input: --minimum-allocation-area-size=8193K
+15: minimum-allocation-area-size A UNSAFE
+expected: 2048 actual: 2048
+
+(TEST) input: --minimum-allocation-area-size=2M
+15: minimum-allocation-area-size A UNSAFE
+expected: 512 actual: 512
+
+(TEST) input: -A9G
+15: minimum-allocation-area-size A UNSAFE
+expected: 2359296 actual: 2359296
+
+(TEST) input: -A0.125G
+15: minimum-allocation-area-size A UNSAFE
+expected: 32768 actual: 32768
+
+(TEST) input: -A3333w
+15: minimum-allocation-area-size A UNSAFE
+expected: 6 actual: 6
+
+(FAIL_TEST) input: -A
+unknown RTS option: -A
+
+(FAIL_TEST) input: --minimum-allocation-area-size
+unknown RTS option: --minimum-allocation-area-size
+
+(FAIL_TEST) input: --minimum-allocation-area-size=
+unknown RTS option: --minimum-allocation-area-size=
+
+(TEST) input: --io-manager-threads=1
+17: io-manager-threads (null) UNSAFE
+expected: 1 actual: 1
+
+(TEST) input: --io-manager-threads=1234567
+17: io-manager-threads (null) UNSAFE
+expected: 1234567 actual: 1234567
+
+(FAIL_TEST) input: --io-manager-threads
+unknown RTS option: --io-manager-threads
+
+(FAIL_TEST) input: --io-manager-threads=
+unknown RTS option: --io-manager-threads=
+
+(TEST) input: --numa
+18: numa (null) SAFE
+expected: 18446744073709551615 actual: 18446744073709551615
+
+(TEST) input: --numa=1
+18: numa (null) SAFE
+expected: 1 actual: 1
+
+(TEST) input: --numa=1234567
+18: numa (null) SAFE
+expected: 1234567 actual: 1234567
+
+(FAIL_TEST) input: --numa=
+unknown RTS option: --numa=
+
+(TEST) input: --debug-numa=1
+19: debug-numa (null) SAFE
+expected: 1 actual: 1
+
+(TEST) input: --debug-numa=8
+19: debug-numa (null) SAFE
+expected: 8 actual: 8
+
+(FAIL_TEST) input: --debug-numa=999
+debug-numa: Too many NUMA nodes (max 16)
+
+(FAIL_TEST) input: --debug-numa999
+unknown RTS option: --debug-numa999
+
+(FAIL_TEST) input: --debug-numa=
+unknown RTS option: --debug-numa=
+
+(FAIL_TEST) input: --debug-numa
+unknown RTS option: --debug-numa
+
+(FAIL_TEST) input: --debug-num
+unknown RTS option: --debug-num
+
+(TEST) input: --long-gc-sync=606.909
+20: long-gc-sync (null) SAFE
+expected: 606.909000 actual: 606.909000
+
+(TEST) input: --long-gc-sync=0.125
+20: long-gc-sync (null) SAFE
+expected: 0.125000 actual: 0.125000
+
+(FAIL_TEST) input: --long-gc-sync
+bad value for long-gc-sync
+
+(FAIL_TEST) input: --long-gc-sync=
+unknown RTS option: --long-gc-sync=
+
+(FAIL_TEST) input: --long-gc-sync=true
+bad value for long-gc-sync=true
+
+(FAIL_TEST) input: --long-gc-syncysaftrweasfasf
+unknown RTS option: --long-gc-syncysaftrweasfasf
+
+(TEST) input: --no-automatic-heap-samples
+21: no-automatic-heap-samples (null) UNSAFE
+
+(TEST) input: --no-automatic-heap-samples=yes
+21: no-automatic-heap-samples (null) UNSAFE
+
+(TEST) input: --no-automatic-heap-samples=no
+21: no-automatic-heap-samples (null) UNSAFE
+
+(FAIL_TEST) input: --no-automatic-heap-samples=
+unknown RTS option: --no-automatic-heap-samples=
+
+(FAIL_TEST) input: --no-automatic-heap-samples=foo
+unknown RTS option: --no-automatic-heap-samples=foo
+
+(FAIL_TEST) input: --no-automatic-heap-samplasfsg
+unknown RTS option: --no-automatic-heap-samplasfsg
+
+(TEST) input: --alloc-area-chunksize=16M
+22: alloc-area-chunksize n UNSAFE
+expected: 4096 actual: 4096
+
+(TEST) input: -n16m
+22: alloc-area-chunksize n UNSAFE
+expected: 4096 actual: 4096
+
+(TEST) input: --alloc-area-chunksize=1234567
+22: alloc-area-chunksize n UNSAFE
+expected: 301 actual: 301
+
+(TEST) input: -n1239999
+22: alloc-area-chunksize n UNSAFE
+expected: 302 actual: 302
+
+(TEST) input: --alloc-area-chunksize=0.225G
+22: alloc-area-chunksize n UNSAFE
+expected: 58982 actual: 58982
+
+(TEST) input: -n99999999k
+22: alloc-area-chunksize n UNSAFE
+expected: 24999999 actual: 24999999
+
+(TEST) input: --alloc-area-chunksize=7654W
+22: alloc-area-chunksize n UNSAFE
+expected: 14 actual: 14
+
+(TEST) input: -n4567w
+22: alloc-area-chunksize n UNSAFE
+expected: 8 actual: 8
+
+(FAIL_TEST) input: --alloc-area-chunksize
+unknown RTS option: --alloc-area-chunksize
+
+(FAIL_TEST) input: --alloc-area-chunksizr
+unknown RTS option: --alloc-area-chunksizr
+
+(FAIL_TEST) input: --alloc-area-chunksizr=23M
+unknown RTS option: --alloc-area-chunksizr=23M
+
+(FAIL_TEST) input: --alloc-area-chunksize=
+unknown RTS option: --alloc-area-chunksize=
+
+(FAIL_TEST) input: -n
+unknown RTS option: -n
+
+(FAIL_TEST) input: -n=
+unknown RTS option: -n=
+
+(FAIL_TEST) input: -n=0
+unknown RTS option: -n=0
+
+(TEST) input: -B
+16: gc-bell B UNSAFE
+
+(TEST) input: --gc-bell
+16: gc-bell B UNSAFE
+
+(FAIL_TEST) input: --gc-bell=
+unknown RTS option: --gc-bell=
+
+(FAIL_TEST) input: --gc-bell=123G
+flag gc-bell given an argument when none was expected: --gc-bell=123G
+
+(FAIL_TEST) input: -B123G
+flag B given an argument when none was expected: -B123G
+
+(TEST) input: --compact-gc
+23: compact-gc c UNSAFE
+expected: 0.000000 actual: 0.000000
+
+(TEST) input: -c
+23: compact-gc c UNSAFE
+expected: 0.000000 actual: 0.000000
+
+(TEST) input: --compact-gc=1125
+23: compact-gc c UNSAFE
+expected: 1125.000000 actual: 1125.000000
+
+(TEST) input: -c
+23: compact-gc c UNSAFE
+expected: 0.000000 actual: 0.000000
+
+(FAIL_TEST) input: --compact-gc=
+unknown RTS option: --compact-gc=
+
+(FAIL_TEST) input: --compact-gc=blah
+bad value for compact-gc=blah
diff --git a/testsuite/tests/rts/OptParseTest.stdout b/testsuite/tests/rts/OptParseTest.stdout
new file mode 100644
index 0000000000..25b32f24e2
--- /dev/null
+++ b/testsuite/tests/rts/OptParseTest.stdout
@@ -0,0 +1,269 @@
+=== OptParseTest START ===
+
+(TEST) input: -?
+0: (null) ? SAFE
+
+(TEST) input: --install-signal-handlers
+1: install-signal-handlers (null) UNSAFE
+ value: true
+
+(TEST) input: --install-signal-handlers=yes
+1: install-signal-handlers (null) UNSAFE
+ value: true
+
+(TEST) input: --install-signal-handlers=no
+1: install-signal-handlers (null) UNSAFE
+ value: false
+
+(TEST) input: --install-seh-handlers
+2: install-seh-handlers (null) UNSAFE
+ value: true
+
+(TEST) input: --install-seh-handlers=yes
+2: install-seh-handlers (null) UNSAFE
+ value: true
+
+(TEST) input: --install-seh-handlers=no
+2: install-seh-handlers (null) UNSAFE
+ value: false
+
+(TEST) input: --generate-stack-traces
+3: generate-stack-traces (null) UNSAFE
+ value: true
+
+(TEST) input: --generate-stack-traces=yes
+3: generate-stack-traces (null) UNSAFE
+ value: true
+
+(TEST) input: --generate-stack-traces=no
+3: generate-stack-traces (null) UNSAFE
+ value: false
+
+(TEST) input: --generate-crash-dumps
+4: generate-crash-dumps (null) UNSAFE
+ value: true
+
+(TEST) input: --generate-crash-dumps=yes
+4: generate-crash-dumps (null) UNSAFE
+ value: true
+
+(TEST) input: --generate-crash-dumps=no
+4: generate-crash-dumps (null) UNSAFE
+ value: false
+
+(TEST) input: --null-eventlog-writer
+5: null-eventlog-writer (null) UNSAFE
+ value: true
+
+(TEST) input: --null-eventlog-writer=yes
+5: null-eventlog-writer (null) UNSAFE
+ value: true
+
+(TEST) input: --null-eventlog-writer=no
+5: null-eventlog-writer (null) UNSAFE
+ value: false
+
+(TEST) input: --machine-readable
+6: machine-readable (null) UNSAFE
+ value: true
+
+(TEST) input: --machine-readable=yes
+6: machine-readable (null) UNSAFE
+ value: true
+
+(TEST) input: --machine-readable=no
+6: machine-readable (null) UNSAFE
+ value: false
+
+(TEST) input: --disable-delayed-os-memory-return
+7: disable-delayed-os-memory-return (null) UNSAFE
+ value: true
+
+(TEST) input: --disable-delayed-os-memory-return=yes
+7: disable-delayed-os-memory-return (null) UNSAFE
+ value: true
+
+(TEST) input: --disable-delayed-os-memory-return=no
+7: disable-delayed-os-memory-return (null) UNSAFE
+ value: false
+
+(TEST) input: --internal-counters
+8: internal-counters (null) SAFE
+ value: true
+
+(TEST) input: --internal-counters=yes
+8: internal-counters (null) SAFE
+ value: true
+
+(TEST) input: --internal-counters=no
+8: internal-counters (null) SAFE
+ value: false
+
+(TEST) input: --io-manager=native
+9: io-manager (null) UNSAFE
+ value: 0
+
+(TEST) input: --io-manager=posix
+9: io-manager (null) UNSAFE
+ value: 1
+
+(TEST) input: --info
+10: info (null) SAFE
+
+(TEST) input: --eventlog-flush-interval=606.909
+11: eventlog-flush-interval (null) SAFE
+ value: 606.909000
+
+(TEST) input: --eventlog-flush-interval=0.125
+11: eventlog-flush-interval (null) SAFE
+ value: 0.125000
+
+(TEST) input: --copying-gc
+12: copying-gc (null) SAFE
+
+(TEST) input: --nonmoving-gc
+13: nonmoving-gc (null) SAFE
+
+(TEST) input: --large-object-allocation=8193K
+14: large-object-allocation AL UNSAFE
+ value: 2048
+
+(TEST) input: --large-object-allocation=2M
+14: large-object-allocation AL UNSAFE
+ value: 512
+
+(TEST) input: -AL9G
+14: large-object-allocation AL UNSAFE
+ value: 2359296
+
+(TEST) input: -AL0.125G
+14: large-object-allocation AL UNSAFE
+ value: 32768
+
+(TEST) input: -AL3333w
+14: large-object-allocation AL UNSAFE
+ value: 6
+
+(TEST) input: --minimum-allocation-area-size=8193K
+15: minimum-allocation-area-size A UNSAFE
+ value: 2048
+
+(TEST) input: --minimum-allocation-area-size=2M
+15: minimum-allocation-area-size A UNSAFE
+ value: 512
+
+(TEST) input: -A9G
+15: minimum-allocation-area-size A UNSAFE
+ value: 2359296
+
+(TEST) input: -A0.125G
+15: minimum-allocation-area-size A UNSAFE
+ value: 32768
+
+(TEST) input: -A3333w
+15: minimum-allocation-area-size A UNSAFE
+ value: 6
+
+(TEST) input: --io-manager-threads=1
+17: io-manager-threads (null) UNSAFE
+ value: 1
+
+(TEST) input: --io-manager-threads=1234567
+17: io-manager-threads (null) UNSAFE
+ value: 1234567
+
+(TEST) input: --numa
+18: numa (null) SAFE
+ value: 18446744073709551615
+
+(TEST) input: --numa=1
+18: numa (null) SAFE
+ value: 1
+
+(TEST) input: --numa=1234567
+18: numa (null) SAFE
+ value: 1234567
+
+(TEST) input: --debug-numa=1
+19: debug-numa (null) SAFE
+ value: 1
+
+(TEST) input: --debug-numa=8
+19: debug-numa (null) SAFE
+ value: 8
+
+(TEST) input: --long-gc-sync=606.909
+20: long-gc-sync (null) SAFE
+ value: 606.909000
+
+(TEST) input: --long-gc-sync=0.125
+20: long-gc-sync (null) SAFE
+ value: 0.125000
+
+(TEST) input: --no-automatic-heap-samples
+21: no-automatic-heap-samples (null) UNSAFE
+ value: true
+
+(TEST) input: --no-automatic-heap-samples=yes
+21: no-automatic-heap-samples (null) UNSAFE
+ value: true
+
+(TEST) input: --no-automatic-heap-samples=no
+21: no-automatic-heap-samples (null) UNSAFE
+ value: false
+
+(TEST) input: --alloc-area-chunksize=16M
+22: alloc-area-chunksize n UNSAFE
+ value: 4096
+
+(TEST) input: -n16m
+22: alloc-area-chunksize n UNSAFE
+ value: 4096
+
+(TEST) input: --alloc-area-chunksize=1234567
+22: alloc-area-chunksize n UNSAFE
+ value: 301
+
+(TEST) input: -n1239999
+22: alloc-area-chunksize n UNSAFE
+ value: 302
+
+(TEST) input: --alloc-area-chunksize=0.225G
+22: alloc-area-chunksize n UNSAFE
+ value: 58982
+
+(TEST) input: -n99999999k
+22: alloc-area-chunksize n UNSAFE
+ value: 24999999
+
+(TEST) input: --alloc-area-chunksize=7654W
+22: alloc-area-chunksize n UNSAFE
+ value: 14
+
+(TEST) input: -n4567w
+22: alloc-area-chunksize n UNSAFE
+ value: 8
+
+(TEST) input: -B
+16: gc-bell B UNSAFE
+
+(TEST) input: --gc-bell
+16: gc-bell B UNSAFE
+
+(TEST) input: --compact-gc
+23: compact-gc c UNSAFE
+ value: 0.000000
+
+(TEST) input: -c
+23: compact-gc c UNSAFE
+ value: 0.000000
+
+(TEST) input: --compact-gc=1125
+23: compact-gc c UNSAFE
+ value: 1125.000000
+
+(TEST) input: -c
+23: compact-gc c UNSAFE
+ value: 0.000000
+
+=== OptParseTest END ===
diff --git a/testsuite/tests/rts/all.T b/testsuite/tests/rts/all.T
index 92585a9f07..d076368920 100644
--- a/testsuite/tests/rts/all.T
+++ b/testsuite/tests/rts/all.T
@@ -564,3 +564,5 @@ test('decodeMyStack_emptyListForMissingFlag',
, ignore_stderr
, js_broken(22261) # cloneMyStack# not yet implemented
], compile_and_run, [''])
+
+test('OptParseTest', [ c_src, only_ways(['normal','threaded1', 'threaded2']) ], compile_and_run, [''])