rts: enable parallel GC scan of large (32M+) allocation area
[ghc.git] / rts / RtsFlags.c
index 650c4f9..7e06d84 100644 (file)
@@ -13,6 +13,9 @@
 #include "RtsUtils.h"
 #include "Profiling.h"
 #include "RtsFlags.h"
 #include "RtsUtils.h"
 #include "Profiling.h"
 #include "RtsFlags.h"
+#include "sm/OSMem.h"
+#include "hooks/Hooks.h"
+#include "Capability.h"
 
 #ifdef HAVE_CTYPE_H
 #include <ctype.h>
 
 #ifdef HAVE_CTYPE_H
 #include <ctype.h>
@@ -41,6 +44,7 @@ char  **full_prog_argv = NULL;
 char   *prog_name = NULL; /* 'basename' of prog_argv[0] */
 int     rts_argc = 0;  /* ditto */
 char  **rts_argv = NULL;
 char   *prog_name = NULL; /* 'basename' of prog_argv[0] */
 int     rts_argc = 0;  /* ditto */
 char  **rts_argv = NULL;
+int     rts_argv_size = 0;
 #if defined(mingw32_HOST_OS)
 // On Windows, we want to use GetCommandLineW rather than argc/argv,
 // but we need to mutate the command line arguments for withProgName and
 #if defined(mingw32_HOST_OS)
 // On Windows, we want to use GetCommandLineW rather than argc/argv,
 // but we need to mutate the command line arguments for withProgName and
@@ -50,8 +54,26 @@ int       win32_prog_argc = 0;
 wchar_t **win32_prog_argv = NULL;
 #endif
 
 wchar_t **win32_prog_argv = NULL;
 #endif
 
+// The global rtsConfig, set from the RtsConfig supplied by the call
+// to hs_init_ghc().
+RtsConfig rtsConfig;
+
+const RtsConfig defaultRtsConfig  = {
+    .rts_opts_enabled = RtsOptsSafeOnly,
+    .rts_opts_suggestions = rtsTrue,
+    .rts_opts = NULL,
+    .rts_hs_main = rtsFalse,
+    .keep_cafs = rtsFalse,
+    .defaultsHook = FlagDefaultsHook,
+    .onExitHook = OnExitHook,
+    .stackOverflowHook = StackOverflowHook,
+    .outOfHeapHook = OutOfHeapHook,
+    .mallocFailHook = MallocFailHook,
+    .gcDoneHook = NULL
+};
+
 /*
 /*
- * constants, used later 
+ * constants, used later
  */
 #define RTS 1
 #define PGM 0
  */
 #define RTS 1
 #define PGM 0
@@ -60,29 +82,40 @@ wchar_t **win32_prog_argv = NULL;
    Static function decls
    -------------------------------------------------------------------------- */
 
    Static function decls
    -------------------------------------------------------------------------- */
 
-static void procRtsOpts      (int rts_argc0, RtsOptsEnabledEnum enabled);
+static void procRtsOpts (int rts_argc0, RtsOptsEnabledEnum enabled);
 
 static void normaliseRtsOpts (void);
 
 
 static void normaliseRtsOpts (void);
 
-static void initStatsFile    (FILE *f);
+static void initStatsFile (FILE *f);
+
+static int  openStatsFile (
+    char *filename, const char *FILENAME_FMT, FILE **file_ret);
 
 
-static int  openStatsFile    (char *filename, const char *FILENAME_FMT,
-                              FILE **file_ret);
+static StgWord64 decodeSize (
+    const char *flag, uint32_t offset, StgWord64 min, StgWord64 max);
 
 
-static StgWord64 decodeSize  (const char *flag, nat offset,
-                              StgWord64 min, StgWord64 max);
+static void bad_option (const char *s);
 
 
-static void bad_option       (const char *s);
+#ifdef DEBUG
+static void read_debug_flags(const char *arg);
+#endif
+
+#ifdef PROFILING
+static rtsBool read_heap_profiling_flag(const char *arg);
+#endif
 
 #ifdef TRACING
 
 #ifdef TRACING
-static void read_trace_flags(char *arg);
+static void read_trace_flags(const char *arg);
 #endif
 
 #endif
 
-static void errorUsage      (void) GNU_ATTRIBUTE(__noreturn__);
+static void errorUsage (void) GNU_ATTRIBUTE(__noreturn__);
 
 
-static char *  copyArg  (char *arg);
+static char *  copyArg (char *arg);
 static char ** copyArgv (int argc, char *argv[]);
 static void    freeArgv (int argc, char *argv[]);
 static char ** copyArgv (int argc, char *argv[]);
 static void    freeArgv (int argc, char *argv[]);
+static void setProgName (char *argv[]);
+
+static void errorRtsOptsDisabled (const char *s);
 
 /* -----------------------------------------------------------------------------
  * Command-line option parsing routines.
 
 /* -----------------------------------------------------------------------------
  * Command-line option parsing routines.
@@ -90,63 +123,63 @@ static void    freeArgv (int argc, char *argv[]);
 
 void initRtsFlagsDefaults(void)
 {
 
 void initRtsFlagsDefaults(void)
 {
-    RtsFlags.GcFlags.statsFile         = NULL;
-    RtsFlags.GcFlags.giveStats         = NO_GC_STATS;
+    StgWord64 maxStkSize = 8 * getPhysicalMemorySize() / 10;
+    // if getPhysicalMemorySize fails just move along with an 8MB limit
+    if (maxStkSize == 0)
+        maxStkSize = 8 * 1024 * 1024;
 
 
-    RtsFlags.GcFlags.maxStkSize                = (8 * 1024 * 1024) / sizeof(W_);
-    RtsFlags.GcFlags.initialStkSize    = 1024 / sizeof(W_);
+    RtsFlags.GcFlags.statsFile          = NULL;
+    RtsFlags.GcFlags.giveStats          = NO_GC_STATS;
+
+    RtsFlags.GcFlags.maxStkSize         = maxStkSize / sizeof(W_);
+    RtsFlags.GcFlags.initialStkSize     = 1024 / sizeof(W_);
     RtsFlags.GcFlags.stkChunkSize       = (32 * 1024) / sizeof(W_);
     RtsFlags.GcFlags.stkChunkBufferSize = (1 * 1024) / sizeof(W_);
 
     RtsFlags.GcFlags.minAllocAreaSize   = (512 * 1024)        / BLOCK_SIZE;
     RtsFlags.GcFlags.stkChunkSize       = (32 * 1024) / sizeof(W_);
     RtsFlags.GcFlags.stkChunkBufferSize = (1 * 1024) / sizeof(W_);
 
     RtsFlags.GcFlags.minAllocAreaSize   = (512 * 1024)        / BLOCK_SIZE;
+    RtsFlags.GcFlags.largeAllocLim      = 0; /* defaults to minAllocAreasize */
+    RtsFlags.GcFlags.nurseryChunkSize   = 0;
     RtsFlags.GcFlags.minOldGenSize      = (1024 * 1024)       / BLOCK_SIZE;
     RtsFlags.GcFlags.minOldGenSize      = (1024 * 1024)       / BLOCK_SIZE;
-    RtsFlags.GcFlags.maxHeapSize       = 0;    /* off by default */
-    RtsFlags.GcFlags.heapSizeSuggestion        = 0;    /* none */
+    RtsFlags.GcFlags.maxHeapSize        = 0;    /* off by default */
+    RtsFlags.GcFlags.heapSizeSuggestion = 0;    /* none */
     RtsFlags.GcFlags.heapSizeSuggestionAuto = rtsFalse;
     RtsFlags.GcFlags.heapSizeSuggestionAuto = rtsFalse;
-    RtsFlags.GcFlags.pcFreeHeap                = 3;    /* 3% */
+    RtsFlags.GcFlags.pcFreeHeap         = 3;    /* 3% */
     RtsFlags.GcFlags.oldGenFactor       = 2;
     RtsFlags.GcFlags.generations        = 2;
     RtsFlags.GcFlags.oldGenFactor       = 2;
     RtsFlags.GcFlags.generations        = 2;
-    RtsFlags.GcFlags.squeezeUpdFrames  = rtsTrue;
+    RtsFlags.GcFlags.squeezeUpdFrames   = rtsTrue;
     RtsFlags.GcFlags.compact            = rtsFalse;
     RtsFlags.GcFlags.compactThreshold   = 30.0;
     RtsFlags.GcFlags.sweep              = rtsFalse;
     RtsFlags.GcFlags.compact            = rtsFalse;
     RtsFlags.GcFlags.compactThreshold   = 30.0;
     RtsFlags.GcFlags.sweep              = rtsFalse;
-#ifdef RTS_GTK_FRONTPANEL
-    RtsFlags.GcFlags.frontpanel         = rtsFalse;
-#endif
     RtsFlags.GcFlags.idleGCDelayTime    = USToTime(300000); // 300ms
     RtsFlags.GcFlags.idleGCDelayTime    = USToTime(300000); // 300ms
-
-#if osf3_HOST_OS
-/* ToDo: Perhaps by adjusting this value we can make linking without
- * -static work (i.e., not generate a core-dumping executable)? */
-# if SIZEOF_VOID_P == 8
-    RtsFlags.GcFlags.heapBase           = 0x180000000L;
-# else
-#  error I have no idea where to begin the heap on a non-64-bit osf3 machine.
-# endif
+#ifdef THREADED_RTS
+    RtsFlags.GcFlags.doIdleGC           = rtsTrue;
 #else
 #else
-    RtsFlags.GcFlags.heapBase           = 0;   /* means don't care */
+    RtsFlags.GcFlags.doIdleGC           = rtsFalse;
 #endif
 #endif
-
-#ifdef DEBUG
-    RtsFlags.DebugFlags.scheduler      = rtsFalse;
-    RtsFlags.DebugFlags.interpreter    = rtsFalse;
-    RtsFlags.DebugFlags.weak           = rtsFalse;
-    RtsFlags.DebugFlags.gccafs         = rtsFalse;
-    RtsFlags.DebugFlags.gc             = rtsFalse;
-    RtsFlags.DebugFlags.block_alloc    = rtsFalse;
-    RtsFlags.DebugFlags.sanity         = rtsFalse;
-    RtsFlags.DebugFlags.stable         = rtsFalse;
+    RtsFlags.GcFlags.heapBase           = 0;   /* means don't care */
+    RtsFlags.GcFlags.allocLimitGrace    = (100*1024) / BLOCK_SIZE;
+    RtsFlags.GcFlags.numa               = rtsFalse;
+    RtsFlags.GcFlags.numaMask           = 1;
+
+    RtsFlags.DebugFlags.scheduler       = rtsFalse;
+    RtsFlags.DebugFlags.interpreter     = rtsFalse;
+    RtsFlags.DebugFlags.weak            = rtsFalse;
+    RtsFlags.DebugFlags.gccafs          = rtsFalse;
+    RtsFlags.DebugFlags.gc              = rtsFalse;
+    RtsFlags.DebugFlags.block_alloc     = rtsFalse;
+    RtsFlags.DebugFlags.sanity          = rtsFalse;
+    RtsFlags.DebugFlags.stable          = rtsFalse;
     RtsFlags.DebugFlags.stm             = rtsFalse;
     RtsFlags.DebugFlags.stm             = rtsFalse;
-    RtsFlags.DebugFlags.prof           = rtsFalse;
-    RtsFlags.DebugFlags.apply          = rtsFalse;
-    RtsFlags.DebugFlags.linker         = rtsFalse;
-    RtsFlags.DebugFlags.squeeze                = rtsFalse;
-    RtsFlags.DebugFlags.hpc            = rtsFalse;
-    RtsFlags.DebugFlags.sparks         = rtsFalse;
-#endif
+    RtsFlags.DebugFlags.prof            = rtsFalse;
+    RtsFlags.DebugFlags.apply           = rtsFalse;
+    RtsFlags.DebugFlags.linker          = rtsFalse;
+    RtsFlags.DebugFlags.squeeze         = rtsFalse;
+    RtsFlags.DebugFlags.hpc             = rtsFalse;
+    RtsFlags.DebugFlags.sparks          = rtsFalse;
+    RtsFlags.DebugFlags.numa            = rtsFalse;
 
 #if defined(PROFILING)
 
 #if defined(PROFILING)
-    RtsFlags.CcFlags.doCostCentres     = 0;
+    RtsFlags.CcFlags.doCostCentres      = 0;
 #endif /* PROFILING */
 
     RtsFlags.ProfFlags.doHeapProfile      = rtsFalse;
 #endif /* PROFILING */
 
     RtsFlags.ProfFlags.doHeapProfile      = rtsFalse;
@@ -189,28 +222,24 @@ void initRtsFlagsDefaults(void)
     RtsFlags.MiscFlags.linkerMemBase    = 0;
 
 #ifdef THREADED_RTS
     RtsFlags.MiscFlags.linkerMemBase    = 0;
 
 #ifdef THREADED_RTS
-    RtsFlags.ParFlags.nNodes           = 1;
+    RtsFlags.ParFlags.nCapabilities     = 1;
     RtsFlags.ParFlags.migrate           = rtsTrue;
     RtsFlags.ParFlags.parGcEnabled      = 1;
     RtsFlags.ParFlags.parGcGen          = 0;
     RtsFlags.ParFlags.parGcLoadBalancingEnabled = rtsTrue;
     RtsFlags.ParFlags.migrate           = rtsTrue;
     RtsFlags.ParFlags.parGcEnabled      = 1;
     RtsFlags.ParFlags.parGcGen          = 0;
     RtsFlags.ParFlags.parGcLoadBalancingEnabled = rtsTrue;
-    RtsFlags.ParFlags.parGcLoadBalancingGen = 1;
+    RtsFlags.ParFlags.parGcLoadBalancingGen = ~0u; /* auto, based on -A */
+    RtsFlags.ParFlags.parGcNoSyncWithIdle   = 0;
+    RtsFlags.ParFlags.parGcThreads      = 0; /* defaults to -N */
     RtsFlags.ParFlags.setAffinity       = 0;
 #endif
 
 #if defined(THREADED_RTS)
     RtsFlags.ParFlags.setAffinity       = 0;
 #endif
 
 #if defined(THREADED_RTS)
-    RtsFlags.ParFlags.maxLocalSparks   = 4096;
+    RtsFlags.ParFlags.maxLocalSparks    = 4096;
 #endif /* THREADED_RTS */
 
 #ifdef TICKY_TICKY
 #endif /* THREADED_RTS */
 
 #ifdef TICKY_TICKY
-    RtsFlags.TickyFlags.showTickyStats  = rtsFalse;
-    RtsFlags.TickyFlags.tickyFile       = NULL;
-#endif
-
-#ifdef USE_PAPI
-    /* By default no special measurements taken */
-    RtsFlags.PapiFlags.eventType        = 0;
-    RtsFlags.PapiFlags.numUserEvents    = 0;
+    RtsFlags.TickyFlags.showTickyStats   = rtsFalse;
+    RtsFlags.TickyFlags.tickyFile        = NULL;
 #endif
 }
 
 #endif
 }
 
@@ -229,17 +258,22 @@ usage_text[] = {
 "  -?       Prints this message and exits; the program is not executed",
 "  --info   Print information about the RTS used by this program",
 "",
 "  -?       Prints this message and exits; the program is not executed",
 "  --info   Print information about the RTS used by this program",
 "",
-"  -K<size> Sets the maximum stack size (default 8M)  Egs: -K32k   -K512k",
+"  -K<size>  Sets the maximum stack size (default: 80% of the heap)",
+"            Egs: -K32k -K512k -K8M",
 "  -ki<size> Sets the initial thread stack size (default 1k)  Egs: -ki4k -ki2m",
 "  -kc<size> Sets the stack chunk size (default 32k)",
 "  -kb<size> Sets the stack chunk buffer size (default 1k)",
 "",
 "  -ki<size> Sets the initial thread stack size (default 1k)  Egs: -ki4k -ki2m",
 "  -kc<size> Sets the stack chunk size (default 32k)",
 "  -kb<size> Sets the stack chunk buffer size (default 1k)",
 "",
-"  -A<size> Sets the minimum allocation area size (default 512k) Egs: -A1m -A10k",
-"  -M<size> Sets the maximum heap size (default unlimited)  Egs: -M256k -M1G",
-"  -H<size> Sets the minimum heap size (default 0M)   Egs: -H24m  -H1G",
-"  -m<n>    Minimum % of heap which must be available (default 3%)",
-"  -G<n>    Number of generations (default: 2)",
-"  -c<n>    Use in-place compaction instead of copying in the oldest generation",
+"  -A<size>  Sets the minimum allocation area size (default 512k) Egs: -A1m -A10k",
+"  -AL<size> Sets the amount of large-object memory that can be allocated",
+"            before a GC is triggered (default: the value of -A)",
+"  -n<size>  Allocation area chunk size (0 = disabled, default: 0)",
+"  -O<size>  Sets the minimum size of the old generation (default 1M)",
+"  -M<size>  Sets the maximum heap size (default unlimited)  Egs: -M256k -M1G",
+"  -H<size>  Sets the minimum heap size (default 0M)   Egs: -H24m  -H1G",
+"  -m<n>     Minimum % of heap which must be available (default 3%)",
+"  -G<n>     Number of generations (default: 2)",
+"  -c<n>     Use in-place compaction instead of copying in the oldest generation",
 "           when live data is at least <n>% of the maximum heap size set with",
 "           -M (default: 30%)",
 "  -c       Use in-place compaction for all oldest generation collections",
 "           when live data is at least <n>% of the maximum heap size set with",
 "           -M (default: 30%)",
 "  -c       Use in-place compaction for all oldest generation collections",
@@ -249,13 +283,10 @@ usage_text[] = {
 "  -I<sec>  Perform full GC after <sec> idle time (default: 0.3, 0 == off)",
 #endif
 "",
 "  -I<sec>  Perform full GC after <sec> idle time (default: 0.3, 0 == off)",
 #endif
 "",
-"  -T         Collect GC statistics (useful for in-program statistics access)"
+"  -T         Collect GC statistics (useful for in-program statistics access)",
 "  -t[<file>] One-line GC statistics (if <file> omitted, uses stderr)",
 "  -s[<file>] Summary  GC statistics (if <file> omitted, uses stderr)",
 "  -S[<file>] Detailed GC statistics (if <file> omitted, uses stderr)",
 "  -t[<file>] One-line GC statistics (if <file> omitted, uses stderr)",
 "  -s[<file>] Summary  GC statistics (if <file> omitted, uses stderr)",
 "  -S[<file>] Detailed GC statistics (if <file> omitted, uses stderr)",
-#ifdef RTS_GTK_FRONTPANEL
-"  -f       Display front panel (requires X11 & GTK+)",
-#endif
 "",
 "",
 "  -Z       Don't squeeze out update frames on stack overflow",
 "",
 "",
 "  -Z       Don't squeeze out update frames on stack overflow",
@@ -265,8 +296,6 @@ usage_text[] = {
 "  -p       Time/allocation profile        (output file <program>.prof)",
 "  -P       More detailed Time/Allocation profile",
 "  -Pa      Give information about *all* cost centres",
 "  -p       Time/allocation profile        (output file <program>.prof)",
 "  -P       More detailed Time/Allocation profile",
 "  -Pa      Give information about *all* cost centres",
-
-# if defined(PROFILING)
 "",
 "  -h<break-down> Heap residency profile (hp2ps) (output file <program>.hp)",
 "     break-down: c = cost centre stack (default)",
 "",
 "  -h<break-down> Heap residency profile (hp2ps) (output file <program>.hp)",
 "     break-down: c = cost centre stack (default)",
@@ -285,15 +314,14 @@ usage_text[] = {
 "    -hb<bio>...  closures with specified biographies (lag,drag,void,use)",
 "",
 "  -R<size>       Set the maximum retainer set size (default: 8)",
 "    -hb<bio>...  closures with specified biographies (lag,drag,void,use)",
 "",
 "  -R<size>       Set the maximum retainer set size (default: 8)",
-"", 
+"",
 "  -L<chars>      Maximum length of a cost-centre stack in a heap profile",
 "                 (default: 25)",
 "",
 "  -xt            Include threads (TSOs) in a heap profile",
 "",
 "  -xc      Show current cost centre stack on raising an exception",
 "  -L<chars>      Maximum length of a cost-centre stack in a heap profile",
 "                 (default: 25)",
 "",
 "  -xt            Include threads (TSOs) in a heap profile",
 "",
 "  -xc      Show current cost centre stack on raising an exception",
-# endif
-#endif /* PROFILING or PAR */
+#endif /* PROFILING */
 
 #ifdef TRACING
 "",
 
 #ifdef TRACING
 "",
@@ -303,7 +331,7 @@ usage_text[] = {
 #  endif
 "             where [flags] can contain:",
 "                s    scheduler events",
 #  endif
 "             where [flags] can contain:",
 "                s    scheduler events",
-"                g    GC events",
+"                g    GC and heap events",
 "                p    par spark events (sampled)",
 "                f    par spark events (full detail)",
 "                u    user events (emitted from Haskell code)",
 "                p    par spark events (sampled)",
 "                f    par spark events (full detail)",
 "                u    user events (emitted from Haskell code)",
@@ -359,14 +387,28 @@ usage_text[] = {
 "",
 #endif /* DEBUG */
 #if defined(THREADED_RTS) && !defined(NOSMP)
 "",
 #endif /* DEBUG */
 #if defined(THREADED_RTS) && !defined(NOSMP)
-"  -N<n>     Use <n> processors (default: 1)",
-"  -N        Determine the number of processors to use automatically",
+"  -N[<n>]    Use <n> processors (default: 1, -N alone determines",
+"             the number of processors to use automatically)",
+"  -maxN[<n>] Use up to <n> processors automatically",
 "  -qg[<n>]  Use parallel GC only for generations >= <n>",
 "            (default: 0, -qg alone turns off parallel GC)",
 "  -qb[<n>]  Use load-balancing in the parallel GC only for generations >= <n>",
 "  -qg[<n>]  Use parallel GC only for generations >= <n>",
 "            (default: 0, -qg alone turns off parallel GC)",
 "  -qb[<n>]  Use load-balancing in the parallel GC only for generations >= <n>",
-"            (default: 1, -qb alone turns off load-balancing)",
+"            (default: 1 for -A < 32M, 0 otherwise;"
+"             -qb alone turns off load-balancing)",
+"  -qn<n>    Use <n> threads for parallel GC (defaults to value of -N)",
 "  -qa       Use the OS to set thread affinity (experimental)",
 "  -qm       Don't automatically migrate threads between CPUs",
 "  -qa       Use the OS to set thread affinity (experimental)",
 "  -qm       Don't automatically migrate threads between CPUs",
+"  -qi<n>    If a processor has been idle for the last <n> GCs, do not",
+"            wake it up for a non-load-balancing parallel GC.",
+"            (0 disables,  default: 0)",
+"  --numa[=<node_mask>]",
+"            Use NUMA, nodes given by <node_mask> (default: off)",
+#if defined(DEBUG)
+"  --debug-numa[=<num_nodes>]",
+"            Pretend NUMA: like --numa, but without the system calls.",
+"            Can be used on non-NUMA systems for debugging.",
+"",
+#endif
 #endif
 "  --install-signal-handlers=<yes|no>",
 "            Install signal handlers (default: yes)",
 #endif
 "  --install-signal-handlers=<yes|no>",
 "            Install signal handlers (default: yes)",
@@ -377,19 +419,8 @@ usage_text[] = {
 "  -xm       Base address to mmap memory in the GHCi linker",
 "            (hex; must be <80000000)",
 #endif
 "  -xm       Base address to mmap memory in the GHCi linker",
 "            (hex; must be <80000000)",
 #endif
-#if defined(USE_PAPI)
-"  -aX       CPU performance counter measurements using PAPI",
-"            (use with the -s<file> option).  X is one of:",
-"",
-/* "            y - cycles", */
-"            1 - level 1 cache misses",
-"            2 - level 2 cache misses",
-"            b - branch mispredictions",
-"            s - stalled cycles",
-"            e - cache miss and branch misprediction events",
-"            +PAPI_EVENT   - collect papi preset event PAPI_EVENT",
-"            #NATIVE_EVENT - collect native event NATIVE_EVENT (in hex)",
-#endif
+"  -xq       The allocation limit given to a thread after it receives",
+"            an AllocationLimitExceeded exception. (default: 100k)",
 "",
 "RTS options may also be specified using the GHCRTS environment variable.",
 "",
 "",
 "RTS options may also be specified using the GHCRTS environment variable.",
 "",
@@ -399,12 +430,24 @@ usage_text[] = {
 0
 };
 
 0
 };
 
-STATIC_INLINE rtsBool
-strequal(const char *a, const char * b)
+STATIC_INLINE rtsBool strequal(const char *a, const char * b)
 {
     return(strcmp(a, b) == 0);
 }
 
 {
     return(strcmp(a, b) == 0);
 }
 
+// We can't predict up front how much space we'll need for rts_argv,
+// because it involves parsing ghc_rts_opts and GHCRTS, so we
+// expand it on demand.
+static void appendRtsArg (char *arg)
+{
+    if (rts_argc == rts_argv_size) {
+        rts_argv_size *= 2;
+        rts_argv = stgReallocBytes(rts_argv, rts_argv_size * sizeof (char *),
+                                   "RtsFlags.c:appendRtsArg");
+    }
+    rts_argv[rts_argc++] = arg;
+}
+
 static void splitRtsFlags(const char *s)
 {
     const char *c1, *c2;
 static void splitRtsFlags(const char *s)
 {
     const char *c1, *c2;
@@ -412,21 +455,32 @@ static void splitRtsFlags(const char *s)
 
     c1 = s;
     do {
 
     c1 = s;
     do {
-       while (isspace(*c1)) { c1++; };
-       c2 = c1;
-       while (!isspace(*c2) && *c2 != '\0') { c2++; };
-       
-       if (c1 == c2) { break; }
-       
+        while (isspace(*c1)) { c1++; };
+        c2 = c1;
+        while (!isspace(*c2) && *c2 != '\0') { c2++; };
+
+        if (c1 == c2) { break; }
+
         t = stgMallocBytes(c2-c1+1, "RtsFlags.c:splitRtsFlags()");
         strncpy(t, c1, c2-c1);
         t[c2-c1] = '\0';
         t = stgMallocBytes(c2-c1+1, "RtsFlags.c:splitRtsFlags()");
         strncpy(t, c1, c2-c1);
         t[c2-c1] = '\0';
-        rts_argv[rts_argc++] = t;
+        appendRtsArg(t);
 
 
-       c1 = c2;
+        c1 = c2;
     } while (*c1 != '\0');
 }
     } while (*c1 != '\0');
 }
-    
+
+static void errorRtsOptsDisabled(const char *s)
+{
+    char *advice;
+    if (rtsConfig.rts_hs_main) {
+        advice = "Link with -rtsopts to enable them.";
+    } else {
+        advice = "Use hs_init_with_rtsopts() to enable them.";
+    }
+    errorBelch(s, advice);
+}
+
 /* -----------------------------------------------------------------------------
    Parse the command line arguments, collecting options for the RTS.
 
 /* -----------------------------------------------------------------------------
    Parse the command line arguments, collecting options for the RTS.
 
@@ -442,34 +496,37 @@ static void splitRtsFlags(const char *s)
 
      - prog_name   (global) contains the basename of prog_argv[0]
 
 
      - prog_name   (global) contains the basename of prog_argv[0]
 
+     - rtsConfig   (global) contains the supplied RtsConfig
+
   -------------------------------------------------------------------------- */
 
   -------------------------------------------------------------------------- */
 
-void setupRtsFlags (int *argc, char *argv[],
-                    RtsOptsEnabledEnum rtsOptsEnabled,
-                    const char *ghc_rts_opts)
+void setupRtsFlags (int *argc, char *argv[], RtsConfig rts_config)
 {
 {
-    nat mode;
-    nat total_arg;
-    nat arg, rts_argc0;
+    uint32_t mode;
+    uint32_t total_arg;
+    uint32_t arg, rts_argc0;
+
+    rtsConfig = rts_config;
 
     setProgName (argv);
     total_arg = *argc;
     arg = 1;
 
 
     setProgName (argv);
     total_arg = *argc;
     arg = 1;
 
-    *argc = 1;
+    if (*argc > 1) { *argc = 1; };
     rts_argc = 0;
 
     rts_argc = 0;
 
-    rts_argv = stgCallocBytes(total_arg + 1, sizeof (char *), "setupRtsFlags");
+    rts_argv_size = total_arg + 1;
+    rts_argv = stgMallocBytes(rts_argv_size * sizeof (char *), "setupRtsFlags");
 
     rts_argc0 = rts_argc;
 
 
     rts_argc0 = rts_argc;
 
-    // process arguments from the ghc_rts_opts global variable first.
+    // process arguments from the -with-rtsopts compile-time flag first
     // (arguments from the GHCRTS environment variable and the command
     // line override these).
     {
     // (arguments from the GHCRTS environment variable and the command
     // line override these).
     {
-       if (ghc_rts_opts != NULL) {
-            splitRtsFlags(ghc_rts_opts);
-            // opts from ghc_rts_opts are always enabled:
+        if (rtsConfig.rts_opts != NULL) {
+            splitRtsFlags(rtsConfig.rts_opts);
+            // opts from rts_opts are always enabled:
             procRtsOpts(rts_argc0, RtsOptsAll);
             rts_argc0 = rts_argc;
         }
             procRtsOpts(rts_argc0, RtsOptsAll);
             rts_argc0 = rts_argc;
         }
@@ -478,15 +535,16 @@ void setupRtsFlags (int *argc, char *argv[],
     // process arguments from the GHCRTS environment variable next
     // (arguments from the command line override these).
     {
     // process arguments from the GHCRTS environment variable next
     // (arguments from the command line override these).
     {
-       char *ghc_rts = getenv("GHCRTS");
+        char *ghc_rts = getenv("GHCRTS");
 
 
-       if (ghc_rts != NULL) {
-            if (rtsOptsEnabled == RtsOptsNone) {
-                errorBelch("Warning: Ignoring GHCRTS variable as RTS options are disabled.\n         Link with -rtsopts to enable them.");
+        if (ghc_rts != NULL) {
+            if (rtsConfig.rts_opts_enabled == RtsOptsNone) {
+                errorRtsOptsDisabled(
+                    "Warning: Ignoring GHCRTS variable as RTS options are disabled.\n         %s");
                 // We don't actually exit, just warn
             } else {
                 splitRtsFlags(ghc_rts);
                 // We don't actually exit, just warn
             } else {
                 splitRtsFlags(ghc_rts);
-                procRtsOpts(rts_argc0, rtsOptsEnabled);
+                procRtsOpts(rts_argc0, rtsConfig.rts_opts_enabled);
                 rts_argc0 = rts_argc;
             }
         }
                 rts_argc0 = rts_argc;
             }
         }
@@ -496,37 +554,39 @@ void setupRtsFlags (int *argc, char *argv[],
     //   argv[0] must be PGM argument -- leave in argv
 
     for (mode = PGM; arg < total_arg; arg++) {
     //   argv[0] must be PGM argument -- leave in argv
 
     for (mode = PGM; arg < total_arg; arg++) {
-       // The '--RTS' argument disables all future +RTS ... -RTS processing.
-       if (strequal("--RTS", argv[arg])) {
-           arg++;
-           break;
-       }
-       // The '--' argument is passed through to the program, but
-       // disables all further +RTS ... -RTS processing.
-       else if (strequal("--", argv[arg])) {
-           break;
-       }
-       else if (strequal("+RTS", argv[arg])) {
+        // The '--RTS' argument disables all future +RTS ... -RTS processing.
+        if (strequal("--RTS", argv[arg])) {
+            arg++;
+            break;
+        }
+        // The '--' argument is passed through to the program, but
+        // disables all further +RTS ... -RTS processing.
+        else if (strequal("--", argv[arg])) {
+            break;
+        }
+        else if (strequal("+RTS", argv[arg])) {
             mode = RTS;
         }
             mode = RTS;
         }
-       else if (strequal("-RTS", argv[arg])) {
-           mode = PGM;
-       }
+        else if (strequal("-RTS", argv[arg])) {
+            mode = PGM;
+        }
         else if (mode == RTS) {
         else if (mode == RTS) {
-            rts_argv[rts_argc++] = copyArg(argv[arg]);
+            appendRtsArg(copyArg(argv[arg]));
         }
         else {
             argv[(*argc)++] = argv[arg];
         }
         else {
             argv[(*argc)++] = argv[arg];
-       }
+        }
     }
     // process remaining program arguments
     for (; arg < total_arg; arg++) {
     }
     // process remaining program arguments
     for (; arg < total_arg; arg++) {
-       argv[(*argc)++] = argv[arg];
+        argv[(*argc)++] = argv[arg];
     }
     argv[*argc] = (char *) 0;
     }
     argv[*argc] = (char *) 0;
-    rts_argv[rts_argc] = (char *) 0;
 
 
-    procRtsOpts(rts_argc0, rtsOptsEnabled);
+    procRtsOpts(rts_argc0, rtsConfig.rts_opts_enabled);
+
+    appendRtsArg((char *)0);
+    rts_argc--; // appendRtsArg will have bumped it for the NULL (#7227)
 
     normaliseRtsOpts();
 
 
     normaliseRtsOpts();
 
@@ -535,46 +595,55 @@ void setupRtsFlags (int *argc, char *argv[],
     if (RtsFlags.GcFlags.statsFile != NULL) {
         initStatsFile (RtsFlags.GcFlags.statsFile);
     }
     if (RtsFlags.GcFlags.statsFile != NULL) {
         initStatsFile (RtsFlags.GcFlags.statsFile);
     }
+#ifdef TICKY_TICKY
     if (RtsFlags.TickyFlags.tickyFile != NULL) {
     if (RtsFlags.TickyFlags.tickyFile != NULL) {
-        initStatsFile (RtsFlags.GcFlags.statsFile);
+        initStatsFile (RtsFlags.TickyFlags.tickyFile);
     }
     }
+#endif
 }
 
 /* -----------------------------------------------------------------------------
  * procRtsOpts: Process rts_argv between rts_argc0 and rts_argc.
  * -------------------------------------------------------------------------- */
 
 }
 
 /* -----------------------------------------------------------------------------
  * procRtsOpts: Process rts_argv between rts_argc0 and rts_argc.
  * -------------------------------------------------------------------------- */
 
+#if defined(HAVE_UNISTD_H) && defined(HAVE_SYS_TYPES_H) && !defined(mingw32_HOST_OS)
 static void checkSuid(RtsOptsEnabledEnum enabled)
 {
     if (enabled == RtsOptsSafeOnly) {
 static void checkSuid(RtsOptsEnabledEnum enabled)
 {
     if (enabled == RtsOptsSafeOnly) {
-#if defined(HAVE_UNISTD_H) && defined(HAVE_SYS_TYPES_H) && !defined(mingw32_HOST_OS)
-       /* This doesn't cover linux/posix capabilities like CAP_DAC_OVERRIDE,
-          we'd have to link with -lcap for that. */
+        /* This doesn't cover linux/posix capabilities like CAP_DAC_OVERRIDE,
+           we'd have to link with -lcap for that. */
         if ((getuid() != geteuid()) || (getgid() != getegid())) {
         if ((getuid() != geteuid()) || (getgid() != getegid())) {
-            errorBelch("RTS options are disabled for setuid binaries. Link with -rtsopts to enable them.");
+            errorRtsOptsDisabled(
+                "RTS options are disabled for setuid binaries. %s");
             stg_exit(EXIT_FAILURE);
         }
             stg_exit(EXIT_FAILURE);
         }
-#endif
     }
 }
     }
 }
+#else
+static void checkSuid (RtsOptsEnabledEnum enabled STG_UNUSED)
+{
+}
+#endif
 
 static void checkUnsafe(RtsOptsEnabledEnum enabled)
 {
     if (enabled == RtsOptsSafeOnly) {
 
 static void checkUnsafe(RtsOptsEnabledEnum enabled)
 {
     if (enabled == RtsOptsSafeOnly) {
-        errorBelch("Most RTS options are disabled. Link with -rtsopts to enable them.");
+        errorRtsOptsDisabled("Most RTS options are disabled. %s");
         stg_exit(EXIT_FAILURE);
     }
 }
 
         stg_exit(EXIT_FAILURE);
     }
 }
 
-static void procRtsOpts (int rts_argc0, RtsOptsEnabledEnum rtsOptsEnabled)
+static void procRtsOpts (int rts_argc0,
+                         RtsOptsEnabledEnum rtsOptsEnabled)
 {
     rtsBool error = rtsFalse;
     int arg;
 {
     rtsBool error = rtsFalse;
     int arg;
+    int unchecked_arg_start;
 
     if (!(rts_argc0 < rts_argc)) return;
 
     if (rtsOptsEnabled == RtsOptsNone) {
 
     if (!(rts_argc0 < rts_argc)) return;
 
     if (rtsOptsEnabled == RtsOptsNone) {
-        errorBelch("RTS options are disabled. Link with -rtsopts to enable them.");
+        errorRtsOptsDisabled("RTS options are disabled. %s");
         stg_exit(EXIT_FAILURE);
     }
 
         stg_exit(EXIT_FAILURE);
     }
 
@@ -584,35 +653,39 @@ static void procRtsOpts (int rts_argc0, RtsOptsEnabledEnum rtsOptsEnabled)
     for (arg = rts_argc0; arg < rts_argc; arg++) {
 
         /* We handle RtsOptsSafeOnly mode by declaring each option as
     for (arg = rts_argc0; arg < rts_argc; arg++) {
 
         /* We handle RtsOptsSafeOnly mode by declaring each option as
-          either OPTION_SAFE or OPTION_UNSAFE. To make sure we cover
-          every branch we use an option_checked flag which is reset
-          at the start each iteration and checked at the end. */
+           either OPTION_SAFE or OPTION_UNSAFE. To make sure we cover
+           every branch we use an option_checked flag which is reset
+           at the start each iteration and checked at the end. */
         rtsBool option_checked = rtsFalse;
 
         rtsBool option_checked = rtsFalse;
 
+// See Note [OPTION_SAFE vs OPTION_UNSAFE].
 #define OPTION_SAFE option_checked = rtsTrue;
 #define OPTION_UNSAFE checkUnsafe(rtsOptsEnabled); option_checked = rtsTrue;
 
         if (rts_argv[arg][0] != '-') {
 #define OPTION_SAFE option_checked = rtsTrue;
 #define OPTION_UNSAFE checkUnsafe(rtsOptsEnabled); option_checked = rtsTrue;
 
         if (rts_argv[arg][0] != '-') {
-           fflush(stdout);
-           errorBelch("unexpected RTS argument: %s", rts_argv[arg]);
-           error = rtsTrue;
+            fflush(stdout);
+            errorBelch("unexpected RTS argument: %s", rts_argv[arg]);
+            error = rtsTrue;
 
         } else {
 
         } else {
-
+            /* 0 is dash, 1 is first letter */
+            /* see Trac #9839 */
+            unchecked_arg_start = 1;
             switch(rts_argv[arg][1]) {
 
             switch(rts_argv[arg][1]) {
 
-             /* process: general args, then PROFILING-only ones, then
-                CONCURRENT-only, TICKY-only (same order as defined in
-                RtsFlags.lh); within those groups, mostly in
-                case-insensitive alphabetical order.  Final group is
-                x*, which allows for more options.
-             */
+              /* process: general args, then PROFILING-only ones, then
+                 CONCURRENT-only, TICKY-only (same order as defined in
+                 RtsFlags.lh); within those groups, mostly in
+                 case-insensitive alphabetical order.  Final group is
+                 x*, which allows for more options.
+              */
 
 #ifdef TICKY_TICKY
 # define TICKY_BUILD_ONLY(x) x
 #else
 # define TICKY_BUILD_ONLY(x) \
 
 #ifdef TICKY_TICKY
 # define TICKY_BUILD_ONLY(x) x
 #else
 # define TICKY_BUILD_ONLY(x) \
-errorBelch("the flag %s requires the program to be built with -ticky", rts_argv[arg]); \
+errorBelch("the flag %s requires the program to be built with -ticky", \
+           rts_argv[arg]);                                             \
 error = rtsTrue;
 #endif
 
 error = rtsTrue;
 #endif
 
@@ -620,7 +693,8 @@ error = rtsTrue;
 # define PROFILING_BUILD_ONLY(x)   x
 #else
 # define PROFILING_BUILD_ONLY(x) \
 # define PROFILING_BUILD_ONLY(x)   x
 #else
 # define PROFILING_BUILD_ONLY(x) \
-errorBelch("the flag %s requires the program to be built with -prof", rts_argv[arg]); \
+errorBelch("the flag %s requires the program to be built with -prof", \
+           rts_argv[arg]);                                            \
 error = rtsTrue;
 #endif
 
 error = rtsTrue;
 #endif
 
@@ -628,7 +702,8 @@ error = rtsTrue;
 # define TRACING_BUILD_ONLY(x)   x
 #else
 # define TRACING_BUILD_ONLY(x) \
 # define TRACING_BUILD_ONLY(x)   x
 #else
 # define TRACING_BUILD_ONLY(x) \
-errorBelch("the flag %s requires the program to be built with -eventlog or -debug", rts_argv[arg]); \
+errorBelch("the flag %s requires the program to be built with -eventlog or -debug", \
+           rts_argv[arg]);                                              \
 error = rtsTrue;
 #endif
 
 error = rtsTrue;
 #endif
 
@@ -636,7 +711,8 @@ error = rtsTrue;
 # define THREADED_BUILD_ONLY(x)      x
 #else
 # define THREADED_BUILD_ONLY(x) \
 # define THREADED_BUILD_ONLY(x)      x
 #else
 # define THREADED_BUILD_ONLY(x) \
-errorBelch("the flag %s requires the program to be built with -threaded", rts_argv[arg]); \
+errorBelch("the flag %s requires the program to be built with -threaded", \
+           rts_argv[arg]);                                              \
 error = rtsTrue;
 #endif
 
 error = rtsTrue;
 #endif
 
@@ -644,20 +720,21 @@ error = rtsTrue;
 # define DEBUG_BUILD_ONLY(x) x
 #else
 # define DEBUG_BUILD_ONLY(x) \
 # define DEBUG_BUILD_ONLY(x) x
 #else
 # define DEBUG_BUILD_ONLY(x) \
-errorBelch("the flag %s requires the program to be built with -debug", rts_argv[arg]); \
+errorBelch("the flag %s requires the program to be built with -debug", \
+           rts_argv[arg]);                                             \
 error = rtsTrue;
 #endif
 
 error = rtsTrue;
 #endif
 
-             /* =========== GENERAL ========================== */
-             case '?':
-               OPTION_SAFE;
-               error = rtsTrue;
-               break;
+              /* =========== GENERAL ========================== */
+              case '?':
+                OPTION_SAFE;
+                error = rtsTrue;
+                break;
 
               /* This isn't going to allow us to keep related options
                  together as we add more --* flags. We really need a
                  proper options parser. */
 
               /* This isn't going to allow us to keep related options
                  together as we add more --* flags. We really need a
                  proper options parser. */
-             case '-':
+              case '-':
                   if (strequal("install-signal-handlers=yes",
                                &rts_argv[arg][2])) {
                       OPTION_UNSAFE;
                   if (strequal("install-signal-handlers=yes",
                                &rts_argv[arg][2])) {
                       OPTION_UNSAFE;
@@ -679,442 +756,363 @@ error = rtsTrue;
                       printRtsInfo();
                       stg_exit(0);
                   }
                       printRtsInfo();
                       stg_exit(0);
                   }
+#if defined(THREADED_RTS)
+                  else if (!strncmp("numa", &rts_argv[arg][2], 4)) {
+                      OPTION_SAFE;
+                      StgWord mask;
+                      if (rts_argv[arg][6] == '=') {
+                          mask = (StgWord)strtol(rts_argv[arg]+7,
+                                                 (char **) NULL, 10);
+                      } else {
+                          mask = (StgWord)~0;
+                      }
+                      if (!osNumaAvailable()) {
+                          errorBelch("%s: OS reports NUMA is not available",
+                                     rts_argv[arg]);
+                          error = rtsTrue;
+                          break;
+                      }
+
+                      RtsFlags.GcFlags.numa = rtsTrue;
+                      RtsFlags.GcFlags.numaMask = mask;
+                  }
+#endif
+#if defined(DEBUG) && defined(THREADED_RTS)
+                  else if (!strncmp("debug-numa", &rts_argv[arg][2], 10)) {
+                      OPTION_SAFE;
+                      size_t nNodes;
+                      if (rts_argv[arg][12] == '=' &&
+                          isdigit(rts_argv[arg][13])) {
+                          nNodes = (StgWord)strtol(rts_argv[arg]+13,
+                                                 (char **) NULL, 10);
+                      } else {
+                          errorBelch("%s: missing number of nodes",
+                                     rts_argv[arg]);
+                          error = rtsTrue;
+                          break;
+                      }
+                      if (nNodes > MAX_NUMA_NODES) {
+                          errorBelch("%s: Too many NUMA nodes (max %d)",
+                                     rts_argv[arg], MAX_NUMA_NODES);
+                          error = rtsTrue;
+                      } else {
+                          RtsFlags.GcFlags.numa = rtsTrue;
+                          RtsFlags.DebugFlags.numa = rtsTrue;
+                          RtsFlags.GcFlags.numaMask = (1<<nNodes) - 1;
+                      }
+                  }
+#endif
                   else {
                   else {
-                     OPTION_SAFE;
-                     errorBelch("unknown RTS option: %s",rts_argv[arg]);
-                     error = rtsTrue;
+                      OPTION_SAFE;
+                      errorBelch("unknown RTS option: %s",rts_argv[arg]);
+                      error = rtsTrue;
                   }
                   }
-                 break;
-             case 'A':
-                 OPTION_UNSAFE;
-                  RtsFlags.GcFlags.minAllocAreaSize
-                      = decodeSize(rts_argv[arg], 2, BLOCK_SIZE, HS_INT_MAX)
+                  break;
+              case 'A':
+                  OPTION_UNSAFE;
+                  if (rts_argv[arg][2] == 'L') {
+                      RtsFlags.GcFlags.largeAllocLim
+                          = decodeSize(rts_argv[arg], 3, 2*BLOCK_SIZE,
+                                       HS_INT_MAX) / BLOCK_SIZE;
+                  } else {
+                      // minimum two blocks in the nursery, so that we have one
+                      // to grab for allocate().
+                      RtsFlags.GcFlags.minAllocAreaSize
+                          = decodeSize(rts_argv[arg], 2, 2*BLOCK_SIZE,
+                                       HS_INT_MAX) / BLOCK_SIZE;
+                  }
+                  break;
+              case 'n':
+                  OPTION_UNSAFE;
+                  RtsFlags.GcFlags.nurseryChunkSize
+                      = decodeSize(rts_argv[arg], 2, 2*BLOCK_SIZE, HS_INT_MAX)
                            / BLOCK_SIZE;
                   break;
 
                            / BLOCK_SIZE;
                   break;
 
-#ifdef USE_PAPI
-             case 'a':
-               OPTION_UNSAFE;
-               switch(rts_argv[arg][2]) {
-               case '1':
-                 RtsFlags.PapiFlags.eventType = PAPI_FLAG_CACHE_L1;
-                 break;
-               case '2':
-                 RtsFlags.PapiFlags.eventType = PAPI_FLAG_CACHE_L2;
-                 break;
-               case 'b':
-                 RtsFlags.PapiFlags.eventType = PAPI_FLAG_BRANCH;
-                 break;
-               case 's':
-                 RtsFlags.PapiFlags.eventType = PAPI_FLAG_STALLS;
-                 break;
-               case 'e':
-                 RtsFlags.PapiFlags.eventType = PAPI_FLAG_CB_EVENTS;
-                 break;
-                case '+':
-                case '#':
-                  if (RtsFlags.PapiFlags.numUserEvents >= MAX_PAPI_USER_EVENTS) {
-                      errorBelch("maximum number of PAPI events reached");
-                      stg_exit(EXIT_FAILURE);
+              case 'B':
+                OPTION_UNSAFE;
+                RtsFlags.GcFlags.ringBell = rtsTrue;
+                unchecked_arg_start++;
+                goto check_rest;
+
+              case 'c':
+                  OPTION_UNSAFE;
+                  if (rts_argv[arg][2] != '\0') {
+                      RtsFlags.GcFlags.compactThreshold =
+                          atof(rts_argv[arg]+2);
+                  } else {
+                      RtsFlags.GcFlags.compact = rtsTrue;
                   }
                   }
-                  nat eventNum  = RtsFlags.PapiFlags.numUserEvents++;
-                  char kind     = rts_argv[arg][2];
-                  nat eventKind = kind == '+' ? PAPI_PRESET_EVENT_KIND : PAPI_NATIVE_EVENT_KIND;
-
-                  RtsFlags.PapiFlags.userEvents[eventNum] = rts_argv[arg] + 3;
-                  RtsFlags.PapiFlags.eventType = PAPI_USER_EVENTS;
-                  RtsFlags.PapiFlags.userEventsKind[eventNum] = eventKind;
                   break;
                   break;
-               default:
-                 bad_option( rts_argv[arg] );
-               }
-               break;
-#endif
-
-             case 'B':
-               OPTION_UNSAFE;
-               RtsFlags.GcFlags.ringBell = rtsTrue;
-               break;
-
-             case 'c':
-                 OPTION_UNSAFE;
-                 if (rts_argv[arg][2] != '\0') {
-                     RtsFlags.GcFlags.compactThreshold =
-                         atof(rts_argv[arg]+2);
-                 } else {
-                     RtsFlags.GcFlags.compact = rtsTrue;
-                 }
-                 break;
 
               case 'w':
 
               case 'w':
-               OPTION_UNSAFE;
-               RtsFlags.GcFlags.sweep = rtsTrue;
-               break;
-
-             case 'F':
-               OPTION_UNSAFE;
-               RtsFlags.GcFlags.oldGenFactor = atof(rts_argv[arg]+2);
-             
-               if (RtsFlags.GcFlags.oldGenFactor < 0)
-                 bad_option( rts_argv[arg] );
-               break;
-             
-             case 'D':
+                OPTION_UNSAFE;
+                RtsFlags.GcFlags.sweep = rtsTrue;
+                unchecked_arg_start++;
+                goto check_rest;
+
+              case 'F':
+                OPTION_UNSAFE;
+                RtsFlags.GcFlags.oldGenFactor = atof(rts_argv[arg]+2);
+
+                if (RtsFlags.GcFlags.oldGenFactor < 0)
+                  bad_option( rts_argv[arg] );
+                break;
+
+              case 'D':
               OPTION_SAFE;
               OPTION_SAFE;
-              DEBUG_BUILD_ONLY(
-             { 
-                 char *c;
-
-                 for (c  = rts_argv[arg] + 2; *c != '\0'; c++) {
-                     switch (*c) {
-                     case 's':
-                         RtsFlags.DebugFlags.scheduler = rtsTrue;
-                         break;
-                     case 'i':
-                         RtsFlags.DebugFlags.interpreter = rtsTrue;
-                         break;
-                     case 'w':
-                         RtsFlags.DebugFlags.weak = rtsTrue;
-                         break;
-                     case 'G':
-                         RtsFlags.DebugFlags.gccafs = rtsTrue;
-                         break;
-                     case 'g':
-                         RtsFlags.DebugFlags.gc = rtsTrue;
-                         break;
-                     case 'b':
-                         RtsFlags.DebugFlags.block_alloc = rtsTrue;
-                         break;
-                     case 'S':
-                         RtsFlags.DebugFlags.sanity = rtsTrue;
-                         break;
-                     case 't':
-                         RtsFlags.DebugFlags.stable = rtsTrue;
-                         break;
-                     case 'p':
-                         RtsFlags.DebugFlags.prof = rtsTrue;
-                         break;
-                     case 'l':
-                         RtsFlags.DebugFlags.linker = rtsTrue;
-                         break;
-                     case 'a':
-                         RtsFlags.DebugFlags.apply = rtsTrue;
-                         break;
-                     case 'm':
-                         RtsFlags.DebugFlags.stm = rtsTrue;
-                         break;
-                     case 'z':
-                         RtsFlags.DebugFlags.squeeze = rtsTrue;
-                         break;
-                     case 'c':
-                         RtsFlags.DebugFlags.hpc = rtsTrue;
-                         break;
-                     case 'r':
-                         RtsFlags.DebugFlags.sparks = rtsTrue;
-                         break;
-                     default:
-                         bad_option( rts_argv[arg] );
-                     }
-                 }
-                  // -Dx also turns on -v.  Use -l to direct trace
-                  // events to the .eventlog file instead.
-                  RtsFlags.TraceFlags.tracing = TRACE_STDERR;
-             })
+              DEBUG_BUILD_ONLY(read_debug_flags(rts_argv[arg]);)
               break;
 
               break;
 
-             case 'K':
-                 OPTION_UNSAFE;
+              case 'K':
+                  OPTION_UNSAFE;
                   RtsFlags.GcFlags.maxStkSize =
                   RtsFlags.GcFlags.maxStkSize =
-                      decodeSize(rts_argv[arg], 2, sizeof(W_), HS_WORD_MAX) / sizeof(W_);
+                      decodeSize(rts_argv[arg], 2, sizeof(W_), HS_WORD_MAX)
+                      / sizeof(W_);
                   break;
 
                   break;
 
-             case 'k':
-               OPTION_UNSAFE;
-               switch(rts_argv[arg][2]) {
+              case 'k':
+                OPTION_UNSAFE;
+                switch(rts_argv[arg][2]) {
                 case 'c':
                   RtsFlags.GcFlags.stkChunkSize =
                 case 'c':
                   RtsFlags.GcFlags.stkChunkSize =
-                      decodeSize(rts_argv[arg], 3, sizeof(W_), HS_WORD_MAX) / sizeof(W_);
+                      decodeSize(rts_argv[arg], 3, sizeof(W_), HS_WORD_MAX)
+                      / sizeof(W_);
                   break;
                 case 'b':
                   RtsFlags.GcFlags.stkChunkBufferSize =
                   break;
                 case 'b':
                   RtsFlags.GcFlags.stkChunkBufferSize =
-                      decodeSize(rts_argv[arg], 3, sizeof(W_), HS_WORD_MAX) / sizeof(W_);
+                      decodeSize(rts_argv[arg], 3, sizeof(W_), HS_WORD_MAX)
+                      / sizeof(W_);
                   break;
                 case 'i':
                   RtsFlags.GcFlags.initialStkSize =
                   break;
                 case 'i':
                   RtsFlags.GcFlags.initialStkSize =
-                      decodeSize(rts_argv[arg], 3, sizeof(W_), HS_WORD_MAX) / sizeof(W_);
+                      decodeSize(rts_argv[arg], 3, sizeof(W_), HS_WORD_MAX)
+                      / sizeof(W_);
                   break;
                 default:
                   RtsFlags.GcFlags.initialStkSize =
                   break;
                 default:
                   RtsFlags.GcFlags.initialStkSize =
-                      decodeSize(rts_argv[arg], 2, sizeof(W_), HS_WORD_MAX) / sizeof(W_);
+                      decodeSize(rts_argv[arg], 2, sizeof(W_), HS_WORD_MAX)
+                      / sizeof(W_);
                   break;
                 }
                 break;
 
               case 'M':
                   break;
                 }
                 break;
 
               case 'M':
-                 OPTION_UNSAFE;
+                  OPTION_UNSAFE;
                   RtsFlags.GcFlags.maxHeapSize =
                   RtsFlags.GcFlags.maxHeapSize =
-                      decodeSize(rts_argv[arg], 2, BLOCK_SIZE, HS_WORD_MAX) / BLOCK_SIZE;
-                  /* user give size in *bytes* but "maxHeapSize" is in *blocks* */
+                      decodeSize(rts_argv[arg], 2, BLOCK_SIZE, HS_WORD_MAX)
+                      / BLOCK_SIZE;
+                  /* user give size in *bytes* but "maxHeapSize" is in
+                   * *blocks* */
                   break;
 
                   break;
 
-             case 'm':
-                 OPTION_UNSAFE;
-                  RtsFlags.GcFlags.pcFreeHeap = atof(rts_argv[arg]+2);
+              case 'm':
+                /* Case for maxN feature request ticket #10728, it's a little
+                   odd being so far from the N case. */
+#if !defined(NOSMP)
+                if (strncmp("maxN", &rts_argv[arg][1], 4) == 0) {
+                  OPTION_SAFE;
+                  THREADED_BUILD_ONLY(
+                    int nCapabilities;
+                    int proc = (int)getNumberOfProcessors();
+
+                    nCapabilities = strtol(rts_argv[arg]+5, (char **) NULL, 10);
+                    if (nCapabilities > proc) { nCapabilities = proc; }
+
+                    if (nCapabilities <= 0) {
+                      errorBelch("bad value for -maxN");
+                      error = rtsTrue;
+                    }
+#if defined(PROFILING)
+                    RtsFlags.ParFlags.nCapabilities = 1;
+#else
+                    RtsFlags.ParFlags.nCapabilities = (uint32_t)nCapabilities;
+#endif
+                  ) break;
+                } else {
+#endif
+                    OPTION_UNSAFE;
+                    RtsFlags.GcFlags.pcFreeHeap = atof(rts_argv[arg]+2);
 
 
-                  if (RtsFlags.GcFlags.pcFreeHeap < 0 ||
-                      RtsFlags.GcFlags.pcFreeHeap > 100)
+                    /* -m was allowing bad flags to go unreported */
+                    if (RtsFlags.GcFlags.pcFreeHeap == 0.0 &&
+                           rts_argv[arg][2] != '0')
                       bad_option( rts_argv[arg] );
                       bad_option( rts_argv[arg] );
-                  break;
 
 
-             case 'G':
-                 OPTION_UNSAFE;
+                    if (RtsFlags.GcFlags.pcFreeHeap < 0 ||
+                        RtsFlags.GcFlags.pcFreeHeap > 100)
+                        bad_option( rts_argv[arg] );
+                    break;
+#if !defined(NOSMP)
+                }
+#endif
+              case 'G':
+                  OPTION_UNSAFE;
                   RtsFlags.GcFlags.generations =
                       decodeSize(rts_argv[arg], 2, 1, HS_INT_MAX);
                   break;
 
                   RtsFlags.GcFlags.generations =
                       decodeSize(rts_argv[arg], 2, 1, HS_INT_MAX);
                   break;
 
-             case 'H':
-                 OPTION_UNSAFE;
+              case 'H':
+                  OPTION_UNSAFE;
                   if (rts_argv[arg][2] == '\0') {
                       RtsFlags.GcFlags.heapSizeSuggestionAuto = rtsTrue;
                   } else {
                   if (rts_argv[arg][2] == '\0') {
                       RtsFlags.GcFlags.heapSizeSuggestionAuto = rtsTrue;
                   } else {
-                      RtsFlags.GcFlags.heapSizeSuggestion =
-                          (nat)(decodeSize(rts_argv[arg], 2, BLOCK_SIZE, HS_WORD_MAX) / BLOCK_SIZE);
+                      RtsFlags.GcFlags.heapSizeSuggestion = (uint32_t)
+                          (decodeSize(rts_argv[arg], 2, BLOCK_SIZE, HS_WORD_MAX)
+                          / BLOCK_SIZE);
                   }
                   break;
 
                   }
                   break;
 
-#ifdef RTS_GTK_FRONTPANEL
-             case 'f':
-                 OPTION_UNSAFE;
-                 RtsFlags.GcFlags.frontpanel = rtsTrue;
-                 break;
-#endif
+              case 'O':
+                  OPTION_UNSAFE;
+                  RtsFlags.GcFlags.minOldGenSize =
+                      (uint32_t)(decodeSize(rts_argv[arg], 2, BLOCK_SIZE,
+                                       HS_WORD_MAX)
+                            / BLOCK_SIZE);
+                  break;
 
 
-             case 'I': /* idle GC delay */
-               OPTION_UNSAFE;
-               if (rts_argv[arg][2] == '\0') {
-                 /* use default */
-               } else {
-                    RtsFlags.GcFlags.idleGCDelayTime =
-                        fsecondsToTime(atof(rts_argv[arg]+2));
-               }
-               break;
+              case 'I': /* idle GC delay */
+                OPTION_UNSAFE;
+                if (rts_argv[arg][2] == '\0') {
+                  /* use default */
+                } else {
+                    Time t = fsecondsToTime(atof(rts_argv[arg]+2));
+                    if (t == 0) {
+                        RtsFlags.GcFlags.doIdleGC = rtsFalse;
+                    } else {
+                        RtsFlags.GcFlags.doIdleGC = rtsTrue;
+                        RtsFlags.GcFlags.idleGCDelayTime = t;
+                    }
+                }
+                break;
 
               case 'T':
 
               case 'T':
-                 OPTION_SAFE;
+                  OPTION_SAFE;
                   RtsFlags.GcFlags.giveStats = COLLECT_GC_STATS;
                   RtsFlags.GcFlags.giveStats = COLLECT_GC_STATS;
-                  break; /* Don't initialize statistics file. */
-
-             case 'S':
-                 OPTION_SAFE; /* but see below */
-                 RtsFlags.GcFlags.giveStats = VERBOSE_GC_STATS;
-                 goto stats;
-
-             case 's':
-                 OPTION_SAFE; /* but see below */
-                 RtsFlags.GcFlags.giveStats = SUMMARY_GC_STATS;
-                 goto stats;
-
-             case 't':
-                 OPTION_SAFE; /* but see below */
-                 RtsFlags.GcFlags.giveStats = ONELINE_GC_STATS;
-                 goto stats;
-
-           stats:
-               { 
-                   int r;
-                   if (rts_argv[arg][2] != '\0') {
-                     OPTION_UNSAFE;
-                   }
+                  unchecked_arg_start++;
+                  goto check_rest; /* Don't initialize statistics file. */
+
+              case 'S':
+                  OPTION_SAFE; /* but see below */
+                  RtsFlags.GcFlags.giveStats = VERBOSE_GC_STATS;
+                  goto stats;
+
+              case 's':
+                  OPTION_SAFE; /* but see below */
+                  RtsFlags.GcFlags.giveStats = SUMMARY_GC_STATS;
+                  goto stats;
+
+              case 't':
+                  OPTION_SAFE; /* but see below */
+                  RtsFlags.GcFlags.giveStats = ONELINE_GC_STATS;
+                  goto stats;
+
+            stats:
+                {
+                    int r;
+                    if (rts_argv[arg][2] != '\0') {
+                      OPTION_UNSAFE;
+                    }
                     r = openStatsFile(rts_argv[arg]+2, NULL,
                                       &RtsFlags.GcFlags.statsFile);
                     r = openStatsFile(rts_argv[arg]+2, NULL,
                                       &RtsFlags.GcFlags.statsFile);
-                   if (r == -1) { error = rtsTrue; }
-               }
+                    if (r == -1) { error = rtsTrue; }
+                }
                 break;
 
                 break;
 
-             case 'Z':
-               OPTION_UNSAFE;
-               RtsFlags.GcFlags.squeezeUpdFrames = rtsFalse;
-               break;
+              case 'Z':
+                OPTION_UNSAFE;
+                RtsFlags.GcFlags.squeezeUpdFrames = rtsFalse;
+                unchecked_arg_start++;
+                goto check_rest;
 
 
-             /* =========== PROFILING ========================== */
+              /* =========== PROFILING ========================== */
 
 
-             case 'P': /* detailed cost centre profiling (time/alloc) */
-             case 'p': /* cost centre profiling (time/alloc) */
-               OPTION_SAFE;
-               PROFILING_BUILD_ONLY(
-               switch (rts_argv[arg][2]) {
+              case 'P': /* detailed cost centre profiling (time/alloc) */
+              case 'p': /* cost centre profiling (time/alloc) */
+                OPTION_SAFE;
+                PROFILING_BUILD_ONLY(
+                switch (rts_argv[arg][2]) {
                   case 'a':
                   case 'a':
-                   RtsFlags.CcFlags.doCostCentres = COST_CENTRES_ALL;
-                   break;
-                 default:
-                     if (rts_argv[arg][1] == 'P') {
-                         RtsFlags.CcFlags.doCostCentres =
-                             COST_CENTRES_VERBOSE;
-                     } else {
-                         RtsFlags.CcFlags.doCostCentres =
-                             COST_CENTRES_SUMMARY;
-                     }
-                     break;
-               }
-               ) break;
-
-             case 'R':
-                 OPTION_SAFE;
-                 PROFILING_BUILD_ONLY(
-                     RtsFlags.ProfFlags.maxRetainerSetSize = atof(rts_argv[arg]+2);
-                 ) break;
-             case 'L':
-                 OPTION_SAFE;
-                 PROFILING_BUILD_ONLY(
-                     RtsFlags.ProfFlags.ccsLength = atof(rts_argv[arg]+2);
+                    RtsFlags.CcFlags.doCostCentres = COST_CENTRES_ALL;
+                    if (rts_argv[arg][3] != '\0') {
+                      errorBelch("flag -Pa given an argument"
+                                 " when none was expected: %s"
+                                ,rts_argv[arg]);
+                      error = rtsTrue;
+                    }
+                    break;
+                  case '\0':
+                      if (rts_argv[arg][1] == 'P') {
+                          RtsFlags.CcFlags.doCostCentres =
+                              COST_CENTRES_VERBOSE;
+                      } else {
+                          RtsFlags.CcFlags.doCostCentres =
+                              COST_CENTRES_SUMMARY;
+                      }
+                      break;
+                  default:
+                    unchecked_arg_start++;
+                    goto check_rest;
+                }
+                ) break;
+
+              case 'R':
+                  OPTION_SAFE;
+                  PROFILING_BUILD_ONLY(
+                      RtsFlags.ProfFlags.maxRetainerSetSize =
+                        atof(rts_argv[arg]+2);
+                  ) break;
+              case 'L':
+                  OPTION_SAFE;
+                  PROFILING_BUILD_ONLY(
+                      RtsFlags.ProfFlags.ccsLength = atof(rts_argv[arg]+2);
                       if(RtsFlags.ProfFlags.ccsLength <= 0) {
                       if(RtsFlags.ProfFlags.ccsLength <= 0) {
-                       bad_option(rts_argv[arg]);
+                        bad_option(rts_argv[arg]);
                       }
                       }
-                 ) break;
-             case 'h': /* serial heap profile */
+                  ) break;
+              case 'h': /* serial heap profile */
 #if !defined(PROFILING)
 #if !defined(PROFILING)
-               OPTION_UNSAFE;
-               switch (rts_argv[arg][2]) {
-                 case '\0':
-                 case 'T':
-                   RtsFlags.ProfFlags.doHeapProfile = HEAP_BY_CLOSURE_TYPE;
-                   break;
-                 default:
-                   errorBelch("invalid heap profile option: %s",rts_argv[arg]);
-                   error = rtsTrue;
-               }
+                switch (rts_argv[arg][2]) {
+                  case '\0':
+                  case 'T':
+                    OPTION_UNSAFE;
+                    RtsFlags.ProfFlags.doHeapProfile = HEAP_BY_CLOSURE_TYPE;
+                    break;
+                  default:
+                    OPTION_SAFE;
+                    PROFILING_BUILD_ONLY();
+                }
 #else
 #else
-               OPTION_SAFE;
-               PROFILING_BUILD_ONLY(
-               switch (rts_argv[arg][2]) {
-               case '\0':
-               case 'C':
-               case 'c':
-               case 'M':
-               case 'm':
-               case 'D':
-               case 'd':
-               case 'Y':
-               case 'y':
-               case 'R':
-               case 'r':
-               case 'B':
-               case 'b':
-                   if (rts_argv[arg][2] != '\0' && rts_argv[arg][3] != '\0') {
-                       {
-                           char *left  = strchr(rts_argv[arg], '{');
-                           char *right = strrchr(rts_argv[arg], '}');
-
-                           // curly braces are optional, for
-                           // backwards compat.
-                           if (left)
-                               left = left+1;
-                           else
-                               left = rts_argv[arg] + 3;
-
-                           if (!right)
-                               right = rts_argv[arg] + strlen(rts_argv[arg]);
-
-                           *right = '\0';
-
-                           switch (rts_argv[arg][2]) {
-                           case 'c': // cost centre label select
-                               RtsFlags.ProfFlags.ccSelector = left;
-                               break;
-                           case 'C':
-                               RtsFlags.ProfFlags.ccsSelector = left;
-                               break;
-                           case 'M':
-                           case 'm': // cost centre module select
-                               RtsFlags.ProfFlags.modSelector = left;
-                               break;
-                           case 'D':
-                           case 'd': // closure descr select 
-                               RtsFlags.ProfFlags.descrSelector = left;
-                               break;
-                           case 'Y':
-                           case 'y': // closure type select
-                               RtsFlags.ProfFlags.typeSelector = left;
-                               break;
-                           case 'R':
-                           case 'r': // retainer select
-                               RtsFlags.ProfFlags.retainerSelector = left;
-                               break;
-                           case 'B':
-                           case 'b': // biography select
-                               RtsFlags.ProfFlags.bioSelector = left;
-                               break;
-                           }
-                       }
-                       break;
-                   }
-
-                   if (RtsFlags.ProfFlags.doHeapProfile != 0) {
-                       errorBelch("multiple heap profile options");
-                       error = rtsTrue;
-                       break;
-                   }
-
-                   switch (rts_argv[arg][2]) {
-                   case '\0':
-                   case 'C':
-                   case 'c':
-                       RtsFlags.ProfFlags.doHeapProfile = HEAP_BY_CCS;
-                       break;
-                   case 'M':
-                   case 'm':
-                         RtsFlags.ProfFlags.doHeapProfile = HEAP_BY_MOD;
-                         break;
-                   case 'D':
-                   case 'd':
-                         RtsFlags.ProfFlags.doHeapProfile = HEAP_BY_DESCR;
-                         break;
-                   case 'Y':
-                   case 'y':
-                         RtsFlags.ProfFlags.doHeapProfile = HEAP_BY_TYPE;
-                         break;
-                   case 'R':
-                   case 'r':
-                         RtsFlags.ProfFlags.doHeapProfile = HEAP_BY_RETAINER;
-                         break;
-                   case 'B':
-                   case 'b':
-                         RtsFlags.ProfFlags.doHeapProfile = HEAP_BY_LDV;
-                         break;
-                   }
-                   break;
-                     
-               default:
-                   errorBelch("invalid heap profile option: %s",rts_argv[arg]);
-                   error = rtsTrue;
-               }
-               ) 
+                OPTION_SAFE;
+                PROFILING_BUILD_ONLY(
+                    error = read_heap_profiling_flag(rts_argv[arg]);
+                );
 #endif /* PROFILING */
 #endif /* PROFILING */
-               break;
+                break;
 
 
-             case 'i': /* heap sample interval */
-               OPTION_UNSAFE;
-               if (rts_argv[arg][2] == '\0') {
-                 /* use default */
-               } else {
+              case 'i': /* heap sample interval */
+                OPTION_UNSAFE;
+                if (rts_argv[arg][2] == '\0') {
+                  /* use default */
+                } else {
                     RtsFlags.ProfFlags.heapProfileInterval =
                         fsecondsToTime(atof(rts_argv[arg]+2));
                 }
                     RtsFlags.ProfFlags.heapProfileInterval =
                         fsecondsToTime(atof(rts_argv[arg]+2));
                 }
-               break;
-
-             /* =========== CONCURRENT ========================= */
-             case 'C': /* context switch interval */
-               OPTION_UNSAFE;
-               if (rts_argv[arg][2] == '\0')
-                   RtsFlags.ConcFlags.ctxtSwitchTime = 0;
-               else {
+                break;
+
+              /* =========== CONCURRENT ========================= */
+              case 'C': /* context switch interval */
+                OPTION_UNSAFE;
+                if (rts_argv[arg][2] == '\0')
+                    RtsFlags.ConcFlags.ctxtSwitchTime = 0;
+                else {
                     RtsFlags.ConcFlags.ctxtSwitchTime =
                         fsecondsToTime(atof(rts_argv[arg]+2));
                 }
                     RtsFlags.ConcFlags.ctxtSwitchTime =
                         fsecondsToTime(atof(rts_argv[arg]+2));
                 }
-               break;
+                break;
 
               case 'V': /* master tick interval */
 
               case 'V': /* master tick interval */
-               OPTION_UNSAFE;
+                OPTION_UNSAFE;
                 if (rts_argv[arg][2] == '\0') {
                     // turns off ticks completely
                     RtsFlags.MiscFlags.tickInterval = 0;
                 if (rts_argv[arg][2] == '\0') {
                     // turns off ticks completely
                     RtsFlags.MiscFlags.tickInterval = 0;
@@ -1125,55 +1123,57 @@ error = rtsTrue;
                 break;
 
 #if !defined(NOSMP)
                 break;
 
 #if !defined(NOSMP)
-             case 'N':
-               OPTION_SAFE;
-               THREADED_BUILD_ONLY(
-               if (rts_argv[arg][2] == '\0') {
+              case 'N':
+                OPTION_SAFE;
+                THREADED_BUILD_ONLY(
+                if (rts_argv[arg][2] == '\0') {
 #if defined(PROFILING)
 #if defined(PROFILING)
-                   RtsFlags.ParFlags.nNodes = 1;
+                    RtsFlags.ParFlags.nCapabilities = 1;
 #else
 #else
-                    RtsFlags.ParFlags.nNodes = getNumberOfProcessors();
+                    RtsFlags.ParFlags.nCapabilities = getNumberOfProcessors();
 #endif
 #endif
-               } else {
-                   int nNodes;
-                   OPTION_SAFE; /* but see extra checks below... */
-                   nNodes = strtol(rts_argv[arg]+2, (char **) NULL, 10);
-                   if (nNodes <= 0) {
-                     errorBelch("bad value for -N");
-                     error = rtsTrue;
-                   }
+                } else {
+                    int nCapabilities;
+                    OPTION_SAFE; /* but see extra checks below... */
+
+                    nCapabilities = strtol(rts_argv[arg]+2, (char **) NULL, 10);
+
+                    if (nCapabilities <= 0) {
+                      errorBelch("bad value for -N");
+                      error = rtsTrue;
+                    }
                     if (rtsOptsEnabled == RtsOptsSafeOnly &&
                     if (rtsOptsEnabled == RtsOptsSafeOnly &&
-                       nNodes > (int)getNumberOfProcessors()) {
-                      errorBelch("Using large values for -N is not allowed by default. Link with -rtsopts to allow full control.");
+                      nCapabilities > (int)getNumberOfProcessors()) {
+                      errorRtsOptsDisabled("Using large values for -N is not allowed by default. %s");
                       stg_exit(EXIT_FAILURE);
                     }
                       stg_exit(EXIT_FAILURE);
                     }
-                    RtsFlags.ParFlags.nNodes = (nat)nNodes;
-               }
-               ) break;
-
-             case 'g':
-               OPTION_UNSAFE;
-               THREADED_BUILD_ONLY(
-                   switch (rts_argv[arg][2]) {
+                    RtsFlags.ParFlags.nCapabilities = (uint32_t)nCapabilities;
+                }
+                ) break;
+
+              case 'g':
+                OPTION_UNSAFE;
+                THREADED_BUILD_ONLY(
+                    switch (rts_argv[arg][2]) {
                     case '1':
                         // backwards compat only
                         RtsFlags.ParFlags.parGcEnabled = rtsFalse;
                         break;
                     case '1':
                         // backwards compat only
                         RtsFlags.ParFlags.parGcEnabled = rtsFalse;
                         break;
-                   default:
-                       errorBelch("unknown RTS option: %s",rts_argv[arg]);
-                       error = rtsTrue;
-                       break;
+                    default:
+                        errorBelch("unknown RTS option: %s",rts_argv[arg]);
+                        error = rtsTrue;
+                        break;
                     }
                     ) break;
 
                     }
                     ) break;
 
-             case 'q':
-               OPTION_UNSAFE;
-               THREADED_BUILD_ONLY(
-                   switch (rts_argv[arg][2]) {
-                   case '\0':
-                       errorBelch("incomplete RTS option: %s",rts_argv[arg]);
-                       error = rtsTrue;
-                       break;
+              case 'q':
+                OPTION_UNSAFE;
+                THREADED_BUILD_ONLY(
+                    switch (rts_argv[arg][2]) {
+                    case '\0':
+                        errorBelch("incomplete RTS option: %s",rts_argv[arg]);
+                        error = rtsTrue;
+                        break;
                     case 'g':
                         if (rts_argv[arg][3] == '\0') {
                             RtsFlags.ParFlags.parGcEnabled = rtsFalse;
                     case 'g':
                         if (rts_argv[arg][3] == '\0') {
                             RtsFlags.ParFlags.parGcEnabled = rtsFalse;
@@ -1183,93 +1183,111 @@ error = rtsTrue;
                                 = strtol(rts_argv[arg]+3, (char **) NULL, 10);
                         }
                         break;
                                 = strtol(rts_argv[arg]+3, (char **) NULL, 10);
                         }
                         break;
-                   case 'b':
+                    case 'b':
                         if (rts_argv[arg][3] == '\0') {
                         if (rts_argv[arg][3] == '\0') {
-                            RtsFlags.ParFlags.parGcLoadBalancingEnabled = rtsFalse;
+                            RtsFlags.ParFlags.parGcLoadBalancingEnabled =
+                                rtsFalse;
                         }
                         else {
                         }
                         else {
-                            RtsFlags.ParFlags.parGcLoadBalancingEnabled = rtsTrue;
+                            RtsFlags.ParFlags.parGcLoadBalancingEnabled =
+                                rtsTrue;
                             RtsFlags.ParFlags.parGcLoadBalancingGen
                                 = strtol(rts_argv[arg]+3, (char **) NULL, 10);
                         }
                         break;
                             RtsFlags.ParFlags.parGcLoadBalancingGen
                                 = strtol(rts_argv[arg]+3, (char **) NULL, 10);
                         }
                         break;
-                   case 'a':
-                       RtsFlags.ParFlags.setAffinity = rtsTrue;
-                       break;
-                   case 'm':
-                       RtsFlags.ParFlags.migrate = rtsFalse;
-                       break;
+                    case 'i':
+                        RtsFlags.ParFlags.parGcNoSyncWithIdle
+                            = strtol(rts_argv[arg]+3, (char **) NULL, 10);
+                        break;
+                    case 'n': {
+                        int threads;
+                        threads = strtol(rts_argv[arg]+3, (char **) NULL, 10);
+                        if (threads <= 0) {
+                            errorBelch("-qn must be 1 or greater");
+                            error = rtsTrue;
+                        } else {
+                            RtsFlags.ParFlags.parGcThreads = threads;
+                        }
+                        break;
+                    }
+                    case 'a':
+                        RtsFlags.ParFlags.setAffinity = rtsTrue;
+                        break;
+                    case 'm':
+                        RtsFlags.ParFlags.migrate = rtsFalse;
+                        break;
                     case 'w':
                         // -qw was removed; accepted for backwards compat
                         break;
                     default:
                     case 'w':
                         // -qw was removed; accepted for backwards compat
                         break;
                     default:
-                       errorBelch("unknown RTS option: %s",rts_argv[arg]);
-                       error = rtsTrue;
-                       break;
-                   }
+                        errorBelch("unknown RTS option: %s",rts_argv[arg]);
+                        error = rtsTrue;
+                        break;
+                    }
                     ) break;
 #endif
                     ) break;
 #endif
-             /* =========== PARALLEL =========================== */
-             case 'e':
-               OPTION_UNSAFE;
-               THREADED_BUILD_ONLY(
-               if (rts_argv[arg][2] != '\0') {
-                   RtsFlags.ParFlags.maxLocalSparks
-                     = strtol(rts_argv[arg]+2, (char **) NULL, 10);
-                   if (RtsFlags.ParFlags.maxLocalSparks <= 0) {
-                     errorBelch("bad value for -e");
-                     error = rtsTrue;
-                   }
-               }
-               ) break;
-
-             /* =========== TICKY ============================== */
-
-             case 'r': /* Basic profiling stats */
-               OPTION_SAFE;
-               TICKY_BUILD_ONLY(
-
-               RtsFlags.TickyFlags.showTickyStats = rtsTrue;
-
-               { 
-                   int r;
-                   if (rts_argv[arg][2] != '\0') {
-                     OPTION_UNSAFE;
-                   }
+              /* =========== PARALLEL =========================== */
+              case 'e':
+                OPTION_UNSAFE;
+                THREADED_BUILD_ONLY(
+                if (rts_argv[arg][2] != '\0') {
+                    RtsFlags.ParFlags.maxLocalSparks
+                      = strtol(rts_argv[arg]+2, (char **) NULL, 10);
+                    if (RtsFlags.ParFlags.maxLocalSparks <= 0) {
+                      errorBelch("bad value for -e");
+                      error = rtsTrue;
+                    }
+                }
+                ) break;
+
+              /* =========== TICKY ============================== */
+
+              case 'r': /* Basic profiling stats */
+                OPTION_SAFE;
+                TICKY_BUILD_ONLY(
+
+                RtsFlags.TickyFlags.showTickyStats = rtsTrue;
+
+                {
+                    int r;
+                    if (rts_argv[arg][2] != '\0') {
+                      OPTION_UNSAFE;
+                    }
                     r = openStatsFile(rts_argv[arg]+2,
                                       TICKY_FILENAME_FMT,
                                       &RtsFlags.TickyFlags.tickyFile);
                     r = openStatsFile(rts_argv[arg]+2,
                                       TICKY_FILENAME_FMT,
                                       &RtsFlags.TickyFlags.tickyFile);
-                   if (r == -1) { error = rtsTrue; }
-               }
-               ) break;
+                    if (r == -1) { error = rtsTrue; }
+                }
+                ) break;
 
 
-             /* =========== TRACING ---------=================== */
+              /* =========== TRACING ---------=================== */
 
               case 'l':
 
               case 'l':
-                 OPTION_SAFE;
+                  OPTION_SAFE;
                   TRACING_BUILD_ONLY(
                       RtsFlags.TraceFlags.tracing = TRACE_EVENTLOG;
                       read_trace_flags(&rts_argv[arg][2]);
                       );
                   break;
 
                   TRACING_BUILD_ONLY(
                       RtsFlags.TraceFlags.tracing = TRACE_EVENTLOG;
                       read_trace_flags(&rts_argv[arg][2]);
                       );
                   break;
 
-             case 'v':
-                 OPTION_SAFE;
+              case 'v':
+                  OPTION_SAFE;
                   DEBUG_BUILD_ONLY(
                       RtsFlags.TraceFlags.tracing = TRACE_STDERR;
                       read_trace_flags(&rts_argv[arg][2]);
                       );
                   break;
 
                   DEBUG_BUILD_ONLY(
                       RtsFlags.TraceFlags.tracing = TRACE_STDERR;
                       read_trace_flags(&rts_argv[arg][2]);
                       );
                   break;
 
-             /* =========== EXTENDED OPTIONS =================== */
+              /* =========== EXTENDED OPTIONS =================== */
 
               case 'x': /* Extend the argument space */
 
               case 'x': /* Extend the argument space */
+                unchecked_arg_start++;
                 switch(rts_argv[arg][2]) {
                   case '\0':
                 switch(rts_argv[arg][2]) {
                   case '\0':
-                   OPTION_SAFE;
-                   errorBelch("incomplete RTS option: %s",rts_argv[arg]);
-                   error = rtsTrue;
-                   break;
+                    OPTION_SAFE;
+                    errorBelch("incomplete RTS option: %s",rts_argv[arg]);
+                    error = rtsTrue;
+                    break;
 
                 case 'b': /* heapBase in hex; undocumented */
                     OPTION_UNSAFE;
 
                 case 'b': /* heapBase in hex; undocumented */
                     OPTION_UNSAFE;
@@ -1298,45 +1316,74 @@ error = rtsTrue;
                     break;
 #endif
 
                     break;
 #endif
 
-                case 'c': /* Debugging tool: show current cost centre on an exception */
+                case 'c': /* Debugging tool: show current cost centre on
+                           an exception */
                     OPTION_SAFE;
                     PROFILING_BUILD_ONLY(
                     OPTION_SAFE;
                     PROFILING_BUILD_ONLY(
-                       RtsFlags.ProfFlags.showCCSOnException = rtsTrue;
-                       );
-                   break;
-
-               case 't':  /* Include memory used by TSOs in a heap profile */
-                   OPTION_SAFE;
-                   PROFILING_BUILD_ONLY(
-                       RtsFlags.ProfFlags.includeTSOs = rtsTrue;
-                       );
-                   break;
-
-                  /* The option prefix '-xx' is reserved for future extension.  KSW 1999-11. */
-
-                 default:
-                   OPTION_SAFE;
-                   errorBelch("unknown RTS option: %s",rts_argv[arg]);
-                   error = rtsTrue;
-                   break;
+                        RtsFlags.ProfFlags.showCCSOnException = rtsTrue;
+                        );
+                    unchecked_arg_start++;
+                    goto check_rest;
+
+                case 't':  /* Include memory used by TSOs in a heap profile */
+                    OPTION_SAFE;
+                    PROFILING_BUILD_ONLY(
+                        RtsFlags.ProfFlags.includeTSOs = rtsTrue;
+                        );
+                    unchecked_arg_start++;
+                    goto check_rest;
+
+                  /*
+                   * The option prefix '-xx' is reserved for future
+                   * extension.  KSW 1999-11.
+                   */
+
+                case 'q':
+                  OPTION_UNSAFE;
+                  RtsFlags.GcFlags.allocLimitGrace
+                      = decodeSize(rts_argv[arg], 3, BLOCK_SIZE, HS_INT_MAX)
+                          / BLOCK_SIZE;
+                  break;
+
+                  default:
+                    OPTION_SAFE;
+                    errorBelch("unknown RTS option: %s",rts_argv[arg]);
+                    error = rtsTrue;
+                    break;
                 }
                 break;  /* defensive programming */
 
                 }
                 break;  /* defensive programming */
 
-             /* =========== OH DEAR ============================ */
-             default:
-               OPTION_SAFE;
-               errorBelch("unknown RTS option: %s",rts_argv[arg]);
-               error = rtsTrue;
-               break;
-           }
+            /* check the rest to be sure there is nothing afterwards.*/
+            /* see Trac #9839 */
+            check_rest:
+                {
+                    /* start checking from the first unchecked position,
+                     * not from index 2*/
+                    /* see Trac #9839 */
+                    if (rts_argv[arg][unchecked_arg_start] != '\0') {
+                      errorBelch("flag -%c given an argument"
+                                 " when none was expected: %s",
+                                 rts_argv[arg][1],rts_argv[arg]);
+                      error = rtsTrue;
+                    }
+                    break;
+                }
+
+              /* =========== OH DEAR ============================ */
+              default:
+                OPTION_SAFE;
+                errorBelch("unknown RTS option: %s",rts_argv[arg]);
+                error = rtsTrue;
+                break;
+            }
 
             if (!option_checked) {
 
             if (!option_checked) {
-               /* Naughty! Someone didn't use OPTION_UNSAFE / OPTION_SAFE for
-                  an option above */
-               errorBelch("Internal error in the RTS options parser");
-               stg_exit(EXIT_FAILURE);
+                /* Naughty! Someone didn't use OPTION_UNSAFE / OPTION_SAFE for
+                   an option above */
+                errorBelch("Internal error in the RTS options parser");
+                stg_exit(EXIT_FAILURE);
             }
             }
-       }
+        }
     }
 
     if (error) errorUsage();
     }
 
     if (error) errorUsage();
@@ -1391,7 +1438,7 @@ static void normaliseRtsOpts (void)
 
     if (RtsFlags.ProfFlags.heapProfileInterval > 0) {
         RtsFlags.ProfFlags.heapProfileIntervalTicks =
 
     if (RtsFlags.ProfFlags.heapProfileInterval > 0) {
         RtsFlags.ProfFlags.heapProfileIntervalTicks =
-            RtsFlags.ProfFlags.heapProfileInterval / 
+            RtsFlags.ProfFlags.heapProfileInterval /
             RtsFlags.MiscFlags.tickInterval;
     } else {
         RtsFlags.ProfFlags.heapProfileIntervalTicks = 0;
             RtsFlags.MiscFlags.tickInterval;
     } else {
         RtsFlags.ProfFlags.heapProfileIntervalTicks = 0;
@@ -1399,9 +1446,33 @@ static void normaliseRtsOpts (void)
 
     if (RtsFlags.GcFlags.stkChunkBufferSize >
         RtsFlags.GcFlags.stkChunkSize / 2) {
 
     if (RtsFlags.GcFlags.stkChunkBufferSize >
         RtsFlags.GcFlags.stkChunkSize / 2) {
-        errorBelch("stack chunk buffer size (-kb) must be less than 50%% of the stack chunk size (-kc)");
+        errorBelch("stack chunk buffer size (-kb) must be less than 50%%\n"
+                   "of the stack chunk size (-kc)");
         errorUsage();
     }
         errorUsage();
     }
+
+    if (RtsFlags.ParFlags.parGcLoadBalancingGen == ~0u) {
+        StgWord alloc_area_bytes
+            = RtsFlags.GcFlags.minAllocAreaSize * BLOCK_SIZE;
+
+        // If allocation area is larger that CPU cache
+        // we can finish scanning quicker doing work-stealing
+        // scan. Trac #9221
+        // 32M looks big enough not to fit into L2 cache
+        // of popular modern CPUs.
+        if (alloc_area_bytes >= 32 * 1024 * 1024) {
+            RtsFlags.ParFlags.parGcLoadBalancingGen = 0;
+        } else {
+            RtsFlags.ParFlags.parGcLoadBalancingGen = 1;
+        }
+    }
+
+#ifdef THREADED_RTS
+    if (RtsFlags.ParFlags.parGcThreads > RtsFlags.ParFlags.nCapabilities) {
+        errorBelch("GC threads (-qn) must be between 1 and the value of -N");
+        errorUsage();
+    }
+#endif
 }
 
 static void errorUsage (void)
 }
 
 static void errorUsage (void)
@@ -1420,9 +1491,9 @@ stats_fprintf(FILE *f, char *s, ...)
     va_list ap;
     va_start(ap,s);
     if (f == NULL) {
     va_list ap;
     va_start(ap,s);
     if (f == NULL) {
-       vdebugBelch(s, ap);
+        vdebugBelch(s, ap);
     } else {
     } else {
-       vfprintf(f, s, ap);
+        vfprintf(f, s, ap);
     }
     va_end(ap);
 }
     }
     va_end(ap);
 }
@@ -1448,14 +1519,15 @@ openStatsFile (char *filename,           // filename, or NULL
         if (*filename != '\0') {  /* stats file specified */
             f = fopen(filename,"w");
         } else {
         if (*filename != '\0') {  /* stats file specified */
             f = fopen(filename,"w");
         } else {
-            char stats_filename[STATS_FILENAME_MAXLEN]; /* default <program>.<ext> */
+            /* default <program>.<ext> */
+            char stats_filename[STATS_FILENAME_MAXLEN];
             sprintf(stats_filename, filename_fmt, prog_name);
             f = fopen(stats_filename,"w");
         }
             sprintf(stats_filename, filename_fmt, prog_name);
             f = fopen(stats_filename,"w");
         }
-       if (f == NULL) {
+        if (f == NULL) {
             errorBelch("Can't open stats file %s\n", filename);
             errorBelch("Can't open stats file %s\n", filename);
-           return -1;
-       }
+            return -1;
+        }
     }
     *file_ret = f;
 
     }
     *file_ret = f;
 
@@ -1485,7 +1557,7 @@ static void initStatsFile (FILE *f)
 -------------------------------------------------------------------------- */
 
 static StgWord64
 -------------------------------------------------------------------------- */
 
 static StgWord64
-decodeSize(const char *flag, nat offset, StgWord64 min, StgWord64 max)
+decodeSize(const char *flag, uint32_t offset, StgWord64 min, StgWord64 max)
 {
     char c;
     const char *s;
 {
     char c;
     const char *s;
@@ -1503,7 +1575,7 @@ decodeSize(const char *flag, nat offset, StgWord64 min, StgWord64 max)
         m = atof(s);
         c = s[strlen(s)-1];
 
         m = atof(s);
         c = s[strlen(s)-1];
 
-        if (c == 'g' || c == 'G') 
+        if (c == 'g' || c == 'G')
             m *= 1024*1024*1024;
         else if (c == 'm' || c == 'M')
             m *= 1024*1024;
             m *= 1024*1024*1024;
         else if (c == 'm' || c == 'M')
             m *= 1024*1024;
@@ -1518,17 +1590,195 @@ decodeSize(const char *flag, nat offset, StgWord64 min, StgWord64 max)
     if (m < 0 || val < min || val > max) {
         // printf doesn't like 64-bit format specs on Windows
         // apparently, so fall back to unsigned long.
     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 (%lu - %lu)", flag, (lnat)min, (lnat)max);
+        errorBelch("error in RTS option %s: size outside allowed range (%" FMT_Word " - %" FMT_Word ")", flag, (W_)min, (W_)max);
         stg_exit(EXIT_FAILURE);
     }
 
     return val;
 }
 
         stg_exit(EXIT_FAILURE);
     }
 
     return val;
 }
 
+#ifdef DEBUG
+static void read_debug_flags(const char* arg)
+{
+    // Already parsed "-D"
+    const char *c;
+    for (c  = arg + 2; *c != '\0'; c++) {
+        switch (*c) {
+        case 's':
+            RtsFlags.DebugFlags.scheduler = rtsTrue;
+            break;
+        case 'i':
+            RtsFlags.DebugFlags.interpreter = rtsTrue;
+            break;
+        case 'w':
+            RtsFlags.DebugFlags.weak = rtsTrue;
+            break;
+        case 'G':
+            RtsFlags.DebugFlags.gccafs = rtsTrue;
+            break;
+        case 'g':
+            RtsFlags.DebugFlags.gc = rtsTrue;
+            break;
+        case 'b':
+            RtsFlags.DebugFlags.block_alloc = rtsTrue;
+            break;
+        case 'S':
+            RtsFlags.DebugFlags.sanity = rtsTrue;
+            break;
+        case 't':
+            RtsFlags.DebugFlags.stable = rtsTrue;
+            break;
+        case 'p':
+            RtsFlags.DebugFlags.prof = rtsTrue;
+            break;
+        case 'l':
+            RtsFlags.DebugFlags.linker = rtsTrue;
+            break;
+        case 'a':
+            RtsFlags.DebugFlags.apply = rtsTrue;
+            break;
+        case 'm':
+            RtsFlags.DebugFlags.stm = rtsTrue;
+            break;
+        case 'z':
+            RtsFlags.DebugFlags.squeeze = rtsTrue;
+            break;
+        case 'c':
+            RtsFlags.DebugFlags.hpc = rtsTrue;
+            break;
+        case 'r':
+            RtsFlags.DebugFlags.sparks = rtsTrue;
+            break;
+        default:
+            bad_option( arg );
+        }
+    }
+    // -Dx also turns on -v.  Use -l to direct trace
+    // events to the .eventlog file instead.
+    RtsFlags.TraceFlags.tracing = TRACE_STDERR;
+}
+#endif
+
+#ifdef PROFILING
+// Parse a "-h" flag, returning whether the parse resulted in an error.
+static rtsBool read_heap_profiling_flag(const char *arg)
+{
+    // Already parsed "-h"
+
+    rtsBool error = rtsFalse;
+    switch (arg[2]) {
+    case '\0':
+    case 'C':
+    case 'c':
+    case 'M':
+    case 'm':
+    case 'D':
+    case 'd':
+    case 'Y':
+    case 'y':
+    case 'R':
+    case 'r':
+    case 'B':
+    case 'b':
+        if (arg[2] != '\0' && arg[3] != '\0') {
+            {
+                const char *left  = strchr(arg, '{');
+                const char *right = strrchr(arg, '}');
+
+                // curly braces are optional, for
+                // backwards compat.
+                if (left)
+                    left = left+1;
+                else
+                    left = arg + 3;
+
+                if (!right)
+                    right = arg + strlen(arg);
+
+                char *selector = stgStrndup(left, right - left + 1);
+
+                switch (arg[2]) {
+                case 'c': // cost centre label select
+                    RtsFlags.ProfFlags.ccSelector = selector;
+                    break;
+                case 'C':
+                    RtsFlags.ProfFlags.ccsSelector = selector;
+                    break;
+                case 'M':
+                case 'm': // cost centre module select
+                    RtsFlags.ProfFlags.modSelector = selector;
+                    break;
+                case 'D':
+                case 'd': // closure descr select
+                    RtsFlags.ProfFlags.descrSelector = selector;
+                    break;
+                case 'Y':
+                case 'y': // closure type select
+                    RtsFlags.ProfFlags.typeSelector = selector;
+                    break;
+                case 'R':
+                case 'r': // retainer select
+                    RtsFlags.ProfFlags.retainerSelector = selector;
+                    break;
+                case 'B':
+                case 'b': // biography select
+                    RtsFlags.ProfFlags.bioSelector = selector;
+                    break;
+                default:
+                    free(selector);
+                }
+            }
+            break;
+        }
+
+        if (RtsFlags.ProfFlags.doHeapProfile != 0) {
+            errorBelch("multiple heap profile options");
+            error = rtsTrue;
+            break;
+        }
+
+        switch (arg[2]) {
+        case '\0':
+        case 'C':
+        case 'c':
+            RtsFlags.ProfFlags.doHeapProfile = HEAP_BY_CCS;
+            break;
+        case 'M':
+        case 'm':
+            RtsFlags.ProfFlags.doHeapProfile = HEAP_BY_MOD;
+            break;
+        case 'D':
+        case 'd':
+            RtsFlags.ProfFlags.doHeapProfile = HEAP_BY_DESCR;
+            break;
+        case 'Y':
+        case 'y':
+            RtsFlags.ProfFlags.doHeapProfile = HEAP_BY_TYPE;
+            break;
+        case 'R':
+        case 'r':
+            RtsFlags.ProfFlags.doHeapProfile = HEAP_BY_RETAINER;
+            break;
+        case 'B':
+        case 'b':
+            RtsFlags.ProfFlags.doHeapProfile = HEAP_BY_LDV;
+            break;
+        }
+        break;
+
+    default:
+        errorBelch("invalid heap profile option: %s", arg);
+        error = rtsTrue;
+    }
+
+    return error;
+}
+#endif
+
 #if defined(TRACING)
 #if defined(TRACING)
-static void read_trace_flags(char *arg)
+static void read_trace_flags(const char *arg)
 {
 {
-    char *c;
+    const char *c;
     rtsBool enabled = rtsTrue;
     /* Syntax for tracing flags currently looks like:
      *
     rtsBool enabled = rtsTrue;
     /* Syntax for tracing flags currently looks like:
      *
@@ -1653,22 +1903,28 @@ static void freeArgv(int argc, char *argv[])
 void
 setProgName(char *argv[])
 {
 void
 setProgName(char *argv[])
 {
+    char *last_slash;
+
+    if (argv[0] == NULL) { // #7037
+        prog_name = "";
+        return;
+    }
+
     /* Remove directory from argv[0] -- default files in current directory */
 #if !defined(mingw32_HOST_OS)
     /* Remove directory from argv[0] -- default files in current directory */
 #if !defined(mingw32_HOST_OS)
-    char *last_slash;
     if ( (last_slash = (char *) strrchr(argv[0], '/')) != NULL ) {
     if ( (last_slash = (char *) strrchr(argv[0], '/')) != NULL ) {
-       prog_name = last_slash+1;
+        prog_name = last_slash+1;
    } else {
    } else {
-       prog_name = argv[0];
+        prog_name = argv[0];
    }
 #else
    }
 #else
-    char* last_slash = argv[0] + (strlen(argv[0]) - 1);
+    last_slash = argv[0] + (strlen(argv[0]) - 1);
     while ( last_slash > argv[0] ) {
     while ( last_slash > argv[0] ) {
-       if ( *last_slash == '/' || *last_slash == '\\' ) {
-           prog_name = last_slash+1;
-           return;
-       }
-       last_slash--;
+        if ( *last_slash == '/' || *last_slash == '\\' ) {
+            prog_name = last_slash+1;
+            return;
+        }
+        last_slash--;
     }
     prog_name = argv[0];
 #endif
     }
     prog_name = argv[0];
 #endif
@@ -1759,16 +2015,16 @@ getWin32ProgArgv(int *argc, wchar_t **argv[])
 void
 setWin32ProgArgv(int argc, wchar_t *argv[])
 {
 void
 setWin32ProgArgv(int argc, wchar_t *argv[])
 {
-       int i;
-    
-       freeWin32ProgArgv();
+        int i;
+
+        freeWin32ProgArgv();
 
     win32_prog_argc = argc;
 
     win32_prog_argc = argc;
-       if (argv == NULL) {
-               win32_prog_argv = NULL;
-               return;
-       }
-       
+        if (argv == NULL) {
+                win32_prog_argv = NULL;
+                return;
+        }
+
     win32_prog_argv = stgCallocBytes(argc + 1, sizeof (wchar_t *),
                                     "setWin32ProgArgv 1");
     for (i = 0; i < argc; i++) {
     win32_prog_argv = stgCallocBytes(argc + 1, sizeof (wchar_t *),
                                     "setWin32ProgArgv 1");
     for (i = 0; i < argc; i++) {
@@ -1790,6 +2046,7 @@ freeRtsArgv(void)
     freeArgv(rts_argc,rts_argv);
     rts_argc = 0;
     rts_argv = NULL;
     freeArgv(rts_argc,rts_argv);
     rts_argc = 0;
     rts_argv = NULL;
+    rts_argv_size = 0;
 }
 
 /* ----------------------------------------------------------------------------
 }
 
 /* ----------------------------------------------------------------------------
@@ -1805,3 +2062,41 @@ void freeRtsArgs(void)
     freeProgArgv();
     freeRtsArgv();
 }
     freeProgArgv();
     freeRtsArgv();
 }
+
+
+/*
+Note [OPTION_SAFE vs OPTION_UNSAFE]
+
+Ticket #3910 originally pointed out that the RTS options are a potential
+security problem. For example the -t -s or -S flags can be used to
+overwrite files. This would be bad in the context of CGI scripts or
+setuid binaries. So we introduced a system where +RTS processing is more
+or less disabled unless you pass the -rtsopts flag at link time.
+
+This scheme is safe enough but it also really annoyes users. They have
+to use -rtsopts in many circumstances: with -threaded to use -N, with
+-eventlog to use -l, with -prof to use any of the profiling flags. Many
+users just set -rtsopts globally or in project .cabal files. Apart from
+annoying users it reduces security because it means that deployed
+binaries will have all RTS options enabled rather than just profiling
+ones.
+
+So now, we relax the set of RTS options that are available in the
+default -rtsopts=some case. For "deployment" ways like vanilla and
+-threaded we remain quite conservative. Only --info -? --help are
+allowed for vanilla. For -threaded, -N and -N<x> are allowed with a
+check that x <= num cpus.
+
+For "developer" ways like -debug, -eventlog, -prof, we allow all the
+options that are special to that way. Some of these allow writing files,
+but the file written is not directly under the control of the attacker.
+For the setuid case (where the attacker would have control over binary
+name, current dir, local symlinks etc) we check if the process is
+running setuid/setgid and refuse all RTS option processing. Users would
+need to use -rtsopts=all in this case.
+
+We are making the assumption that developers will not deploy binaries
+built in the -debug, -eventlog, -prof ways. And even if they do, the
+damage should be limited to DOS, information disclosure and writing
+files like <progname>.eventlog, not arbitrary files.
+*/