RtsFlags.c 62.4 KB
Newer Older
1
/* -----------------------------------------------------------------------------
2 3
 *
 * (c) The AQUA Project, Glasgow University, 1994-1997
4
 * (c) The GHC Team, 1998-2006
5 6 7 8 9
 *
 * Functions for parsing the argument list.
 *
 * ---------------------------------------------------------------------------*/

10
#include "PosixSource.h"
11
#include "Rts.h"
Simon Marlow's avatar
Simon Marlow committed
12

13
#include "RtsUtils.h"
14
#include "Profiling.h"
15
#include "RtsFlags.h"
16
#include "sm/OSMem.h"
17
#include "hooks/Hooks.h"
18

19 20
#ifdef HAVE_CTYPE_H
#include <ctype.h>
21 22
#endif

23 24
#include <string.h>

25 26 27 28 29 30 31 32
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif

#ifdef HAVE_SYS_TYPES_H
#include <sys/types.h>
#endif

33 34
// Flag Structure
RTS_FLAGS RtsFlags;
35 36 37 38

/*
 * Split argument lists
 */
sof's avatar
sof committed
39
int     prog_argc = 0;    /* an "int" so as to match normal "argc" */
40
char  **prog_argv = NULL;
41 42
int     full_prog_argc = 0;    /* an "int" so as to match normal "argc" */
char  **full_prog_argv = NULL;
sof's avatar
sof committed
43
char   *prog_name = NULL; /* 'basename' of prog_argv[0] */
44
int     rts_argc = 0;  /* ditto */
45
char  **rts_argv = NULL;
46
int     rts_argv_size = 0;
47 48 49 50 51 52 53 54
#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
// friends. The System.Environment module achieves that using this bit of
// shared state:
int       win32_prog_argc = 0;
wchar_t **win32_prog_argv = NULL;
#endif
55

56 57 58 59 60 61
// The global rtsConfig, set from the RtsConfig supplied by the call
// to hs_init_ghc().
RtsConfig rtsConfig;

const RtsConfig defaultRtsConfig  = {
    .rts_opts_enabled = RtsOptsSafeOnly,
62
    .rts_opts_suggestions = rtsTrue,
63 64
    .rts_opts = NULL,
    .rts_hs_main = rtsFalse,
65
    .keep_cafs = rtsFalse,
66 67 68 69 70 71 72 73
    .defaultsHook = FlagDefaultsHook,
    .onExitHook = OnExitHook,
    .stackOverflowHook = StackOverflowHook,
    .outOfHeapHook = OutOfHeapHook,
    .mallocFailHook = MallocFailHook,
    .gcDoneHook = NULL
};

74
/*
thoughtpolice's avatar
thoughtpolice committed
75
 * constants, used later
76 77 78 79 80 81 82 83
 */
#define RTS 1
#define PGM 0

/* -----------------------------------------------------------------------------
   Static function decls
   -------------------------------------------------------------------------- */

84
static void procRtsOpts (int rts_argc0, RtsOptsEnabledEnum enabled);
85 86 87

static void normaliseRtsOpts (void);

88
static void initStatsFile (FILE *f);
89

90 91
static int  openStatsFile (
    char *filename, const char *FILENAME_FMT, FILE **file_ret);
92

93 94
static StgWord64 decodeSize (
    const char *flag, nat offset, StgWord64 min, StgWord64 max);
95

96
static void bad_option (const char *s);
97

98 99 100 101 102 103 104 105
#ifdef DEBUG
static void read_debug_flags(const char *arg);
#endif

#ifdef PROFILING
static rtsBool read_heap_profiling_flag(const char *arg);
#endif

106
#ifdef TRACING
107
static void read_trace_flags(const char *arg);
108
#endif
109

110
static void errorUsage (void) GNU_ATTRIBUTE(__noreturn__);
111

112
static char *  copyArg (char *arg);
113 114 115
static char ** copyArgv (int argc, char *argv[]);
static void    freeArgv (int argc, char *argv[]);

116
static void errorRtsOptsDisabled (const char *s);
117

118 119 120 121 122 123
/* -----------------------------------------------------------------------------
 * Command-line option parsing routines.
 * ---------------------------------------------------------------------------*/

void initRtsFlagsDefaults(void)
{
124 125 126
    StgWord64 maxStkSize = 8 * getPhysicalMemorySize() / 10;
    // if getPhysicalMemorySize fails just move along with an 8MB limit
    if (maxStkSize == 0)
127
        maxStkSize = 8 * 1024 * 1024;
128

Austin Seipp's avatar
Austin Seipp committed
129 130
    RtsFlags.GcFlags.statsFile          = NULL;
    RtsFlags.GcFlags.giveStats          = NO_GC_STATS;
131

132
    RtsFlags.GcFlags.maxStkSize         = maxStkSize / sizeof(W_);
Austin Seipp's avatar
Austin Seipp committed
133
    RtsFlags.GcFlags.initialStkSize     = 1024 / sizeof(W_);
134 135
    RtsFlags.GcFlags.stkChunkSize       = (32 * 1024) / sizeof(W_);
    RtsFlags.GcFlags.stkChunkBufferSize = (1 * 1024) / sizeof(W_);
136

137
    RtsFlags.GcFlags.minAllocAreaSize   = (512 * 1024)        / BLOCK_SIZE;
138
    RtsFlags.GcFlags.nurseryChunkSize   = 0;
139
    RtsFlags.GcFlags.minOldGenSize      = (1024 * 1024)       / BLOCK_SIZE;
Austin Seipp's avatar
Austin Seipp committed
140 141
    RtsFlags.GcFlags.maxHeapSize        = 0;    /* off by default */
    RtsFlags.GcFlags.heapSizeSuggestion = 0;    /* none */
142
    RtsFlags.GcFlags.heapSizeSuggestionAuto = rtsFalse;
Austin Seipp's avatar
Austin Seipp committed
143
    RtsFlags.GcFlags.pcFreeHeap         = 3;    /* 3% */
144
    RtsFlags.GcFlags.oldGenFactor       = 2;
145
    RtsFlags.GcFlags.generations        = 2;
Austin Seipp's avatar
Austin Seipp committed
146
    RtsFlags.GcFlags.squeezeUpdFrames   = rtsTrue;
147
    RtsFlags.GcFlags.compact            = rtsFalse;
148
    RtsFlags.GcFlags.compactThreshold   = 30.0;
149
    RtsFlags.GcFlags.sweep              = rtsFalse;
Simon Marlow's avatar
Simon Marlow committed
150
    RtsFlags.GcFlags.idleGCDelayTime    = USToTime(300000); // 300ms
151 152 153 154 155
#ifdef THREADED_RTS
    RtsFlags.GcFlags.doIdleGC           = rtsTrue;
#else
    RtsFlags.GcFlags.doIdleGC           = rtsFalse;
#endif
156

157 158 159 160 161 162 163 164 165 166 167
#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
#else
    RtsFlags.GcFlags.heapBase           = 0;   /* means don't care */
#endif
168
    RtsFlags.GcFlags.allocLimitGrace    = (100*1024) / BLOCK_SIZE;
169

170
#ifdef DEBUG
Austin Seipp's avatar
Austin Seipp committed
171 172 173 174 175 176 177 178
    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;
179
    RtsFlags.DebugFlags.stm             = rtsFalse;
Austin Seipp's avatar
Austin Seipp committed
180 181 182 183 184 185
    RtsFlags.DebugFlags.prof            = rtsFalse;
    RtsFlags.DebugFlags.apply           = rtsFalse;
    RtsFlags.DebugFlags.linker          = rtsFalse;
    RtsFlags.DebugFlags.squeeze         = rtsFalse;
    RtsFlags.DebugFlags.hpc             = rtsFalse;
    RtsFlags.DebugFlags.sparks          = rtsFalse;
186 187
#endif

Simon Marlow's avatar
Simon Marlow committed
188
#if defined(PROFILING)
Austin Seipp's avatar
Austin Seipp committed
189
    RtsFlags.CcFlags.doCostCentres      = 0;
Simon Marlow's avatar
Simon Marlow committed
190
#endif /* PROFILING */
191

192
    RtsFlags.ProfFlags.doHeapProfile      = rtsFalse;
Simon Marlow's avatar
Simon Marlow committed
193
    RtsFlags.ProfFlags. heapProfileInterval = USToTime(100000); // 100ms
194 195

#ifdef PROFILING
196
    RtsFlags.ProfFlags.includeTSOs        = rtsFalse;
197
    RtsFlags.ProfFlags.showCCSOnException = rtsFalse;
198
    RtsFlags.ProfFlags.maxRetainerSetSize = 8;
ravi@bluespec.com's avatar
ravi@bluespec.com committed
199
    RtsFlags.ProfFlags.ccsLength          = 25;
200 201 202 203
    RtsFlags.ProfFlags.modSelector        = NULL;
    RtsFlags.ProfFlags.descrSelector      = NULL;
    RtsFlags.ProfFlags.typeSelector       = NULL;
    RtsFlags.ProfFlags.ccSelector         = NULL;
204
    RtsFlags.ProfFlags.ccsSelector        = NULL;
205 206
    RtsFlags.ProfFlags.retainerSelector   = NULL;
    RtsFlags.ProfFlags.bioSelector        = NULL;
207 208
#endif

209
#ifdef TRACING
210
    RtsFlags.TraceFlags.tracing       = TRACE_NONE;
211 212
    RtsFlags.TraceFlags.timestamp     = rtsFalse;
    RtsFlags.TraceFlags.scheduler     = rtsFalse;
213
    RtsFlags.TraceFlags.gc            = rtsFalse;
214 215
    RtsFlags.TraceFlags.sparks_sampled= rtsFalse;
    RtsFlags.TraceFlags.sparks_full   = rtsFalse;
216
    RtsFlags.TraceFlags.user          = rtsFalse;
Simon Marlow's avatar
Simon Marlow committed
217 218
#endif

Simon Marlow's avatar
Simon Marlow committed
219 220 221 222 223 224 225
#ifdef PROFILING
    // When profiling we want a lot more ticks
    RtsFlags.MiscFlags.tickInterval     = USToTime(1000);  // 1ms
#else
    RtsFlags.MiscFlags.tickInterval     = DEFAULT_TICK_INTERVAL;
#endif
    RtsFlags.ConcFlags.ctxtSwitchTime   = USToTime(20000); // 20ms
226

227
    RtsFlags.MiscFlags.install_signal_handlers = rtsTrue;
228
    RtsFlags.MiscFlags.machineReadable = rtsFalse;
229
    RtsFlags.MiscFlags.linkerMemBase    = 0;
230

231
#ifdef THREADED_RTS
Austin Seipp's avatar
Austin Seipp committed
232
    RtsFlags.ParFlags.nNodes            = 1;
233
    RtsFlags.ParFlags.migrate           = rtsTrue;
234
    RtsFlags.ParFlags.parGcEnabled      = 1;
235 236 237
    RtsFlags.ParFlags.parGcGen          = 0;
    RtsFlags.ParFlags.parGcLoadBalancingEnabled = rtsTrue;
    RtsFlags.ParFlags.parGcLoadBalancingGen = 1;
238
    RtsFlags.ParFlags.parGcNoSyncWithIdle   = 0;
239
    RtsFlags.ParFlags.setAffinity       = 0;
240
#endif
241

Simon Marlow's avatar
Simon Marlow committed
242
#if defined(THREADED_RTS)
Austin Seipp's avatar
Austin Seipp committed
243
    RtsFlags.ParFlags.maxLocalSparks    = 4096;
Simon Marlow's avatar
Simon Marlow committed
244
#endif /* THREADED_RTS */
245 246

#ifdef TICKY_TICKY
Austin Seipp's avatar
Austin Seipp committed
247 248
    RtsFlags.TickyFlags.showTickyStats   = rtsFalse;
    RtsFlags.TickyFlags.tickyFile        = NULL;
249 250 251 252 253 254 255 256 257 258 259 260 261 262 263
#endif
}

static const char *
usage_text[] = {
"",
"Usage: <prog> <args> [+RTS <rtsopts> | -RTS <args>] ... --RTS <args>",
"",
"   +RTS    Indicates run time system options follow",
"   -RTS    Indicates program arguments follow",
"  --RTS    Indicates that ALL subsequent arguments will be given to the",
"           program (including any of these RTS flags)",
"",
"The following run time system options are available:",
"",
264
"  -?       Prints this message and exits; the program is not executed",
265
"  --info   Print information about the RTS used by this program",
266
"",
Jan Stolarek's avatar
Jan Stolarek committed
267 268
"  -K<size>  Sets the maximum stack size (default: 80% of the heap)",
"            Egs: -K32k -K512k -K8M",
269 270 271
"  -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)",
272
"",
273
"  -A<size> Sets the minimum allocation area size (default 512k) Egs: -A1m -A10k",
Simon Marlow's avatar
Simon Marlow committed
274
"  -n<size> Allocation area chunk size (0 = disabled, default: 0)",
275
"  -O<size> Sets the minimum size of the old generation (default 1M)",
276
"  -M<size> Sets the maximum heap size (default unlimited)  Egs: -M256k -M1G",
277
"  -H<size> Sets the minimum heap size (default 0M)   Egs: -H24m  -H1G",
278
"  -m<n>    Minimum % of heap which must be available (default 3%)",
279
"  -G<n>    Number of generations (default: 2)",
280 281 282 283 284
"  -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",
"           (the default is to use copying)",
285
"  -w       Use mark-region for the oldest generation (experimental)",
286
#if defined(THREADED_RTS)
287 288
"  -I<sec>  Perform full GC after <sec> idle time (default: 0.3, 0 == off)",
#endif
289
"",
290
"  -T         Collect GC statistics (useful for in-program statistics access)",
291 292 293
"  -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)",
294 295 296 297
"",
"",
"  -Z       Don't squeeze out update frames on stack overflow",
"  -B       Sound the bell at the start of each garbage collection",
Simon Marlow's avatar
Simon Marlow committed
298
#if defined(PROFILING)
299
"",
300 301 302
"  -p       Time/allocation profile        (output file <program>.prof)",
"  -P       More detailed Time/Allocation profile",
"  -Pa      Give information about *all* cost centres",
303
"",
304 305 306 307 308 309 310
"  -h<break-down> Heap residency profile (hp2ps) (output file <program>.hp)",
"     break-down: c = cost centre stack (default)",
"                 m = module",
"                 d = closure description",
"                 y = type description",
"                 r = retainer",
"                 b = biography (LAG,DRAG,VOID,USE)",
311
"  A subset of closures may be selected thusly:",
312 313
"    -hc<cc>,...  specific cost centre(s) (top of stack only)",
"    -hC<cc>,...  specific cost centre(s) (anywhere in stack)",
314 315 316 317 318
"    -hm<mod>...  all cost centres from the specified modules(s)",
"    -hd<des>,... closures with specified closure descriptions",
"    -hy<typ>...  closures with specified type descriptions",
"    -hr<cc>...   closures with specified retainers",
"    -hb<bio>...  closures with specified biographies (lag,drag,void,use)",
319 320
"",
"  -R<size>       Set the maximum retainer set size (default: 8)",
thoughtpolice's avatar
thoughtpolice committed
321
"",
ravi@bluespec.com's avatar
ravi@bluespec.com committed
322 323
"  -L<chars>      Maximum length of a cost-centre stack in a heap profile",
"                 (default: 25)",
324
"",
325 326
"  -xt            Include threads (TSOs) in a heap profile",
"",
327
"  -xc      Show current cost centre stack on raising an exception",
Ben Gamari's avatar
Ben Gamari committed
328
#endif /* PROFILING */
Simon Marlow's avatar
Simon Marlow committed
329

330
#ifdef TRACING
Simon Marlow's avatar
Simon Marlow committed
331
"",
332 333 334 335 336 337
"  -l[flags]  Log events in binary format to the file <program>.eventlog",
#  ifdef DEBUG
"  -v[flags]  Log events to stderr",
#  endif
"             where [flags] can contain:",
"                s    scheduler events",
338
"                g    GC and heap events",
339 340
"                p    par spark events (sampled)",
"                f    par spark events (full detail)",
341 342
"                u    user events (emitted from Haskell code)",
"                a    all event classes above",
343 344 345
#  ifdef DEBUG
"                t    add time stamps (only useful with -v)",
#  endif
346
"               -x    disable an event class, for any flag above",
347
"             the initial enabled event classes are 'sgpu'",
Simon Marlow's avatar
Simon Marlow committed
348 349
#endif

350
#if !defined(PROFILING)
351
"",
Simon Marlow's avatar
Simon Marlow committed
352
"  -h       Heap residency profile (output file <program>.hp)",
353
#endif
Simon Marlow's avatar
Simon Marlow committed
354
"  -i<sec>  Time between heap profile samples (seconds, default: 0.1)",
355 356
"",
#if defined(TICKY_TICKY)
357
"  -r<file>  Produce ticky-ticky statistics (with -rstderr for stderr)",
358 359
"",
#endif
360 361 362
"  -C<secs>  Context-switch interval in seconds.",
"            0 or no argument means switch as often as possible.",
"            Default: 0.02 sec.",
Simon Marlow's avatar
Simon Marlow committed
363 364 365 366 367 368 369 370
"  -V<secs>  Master tick interval in seconds (0 == disable timer).",
"            This sets the resolution for -C and the heap profile timer -i,",
"            and is the frequence of time profile samples.",
#ifdef PROFILING
"            Default: 0.001 sec.",
#else
"            Default: 0.01 sec.",
#endif
371 372 373
"",
#if defined(DEBUG)
"  -Ds  DEBUG: scheduler",
374
"  -Di  DEBUG: interpreter",
375 376 377 378 379 380 381
"  -Dw  DEBUG: weak",
"  -DG  DEBUG: gccafs",
"  -Dg  DEBUG: gc",
"  -Db  DEBUG: block",
"  -DS  DEBUG: sanity",
"  -Dt  DEBUG: stable",
"  -Dp  DEBUG: prof",
382
"  -Da  DEBUG: apply",
383
"  -Dl  DEBUG: linker",
384
"  -Dm  DEBUG: stm",
Simon Marlow's avatar
Simon Marlow committed
385
"  -Dz  DEBUG: stack squeezing",
386
"  -Dc  DEBUG: program coverage",
387 388
"  -Dr  DEBUG: sparks",
"",
389
"     NOTE: DEBUG events are sent to stderr by default; add -l to create a",
390
"     binary event log file instead.",
391
"",
392
#endif /* DEBUG */
393
#if defined(THREADED_RTS) && !defined(NOSMP)
394 395
"  -N[<n>]   Use <n> processors (default: 1, -N alone determines",
"            the number of processors to use automatically)",
396 397 398 399 400
"  -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)",
"  -qa       Use the OS to set thread affinity (experimental)",
401
"  -qm       Don't automatically migrate threads between CPUs",
402 403 404
"  -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)",
405
#endif
406 407
"  --install-signal-handlers=<yes|no>",
"            Install signal handlers (default: yes)",
Simon Marlow's avatar
Simon Marlow committed
408
#if defined(THREADED_RTS)
409 410
"  -e<n>     Maximum number of outstanding local sparks (default: 4096)",
#endif
411 412 413 414
#if defined(x86_64_HOST_ARCH)
"  -xm       Base address to mmap memory in the GHCi linker",
"            (hex; must be <80000000)",
#endif
415 416
"  -xq       The allocation limit given to a thread after it receives",
"            an AllocationLimitExceeded exception. (default: 100k)",
417
"",
418 419
"RTS options may also be specified using the GHCRTS environment variable.",
"",
420 421 422 423 424 425
"Other RTS options may be available for programs compiled a different way.",
"The GHC User's Guide has full details.",
"",
0
};

426
STATIC_INLINE rtsBool strequal(const char *a, const char * b)
427 428 429 430
{
    return(strcmp(a, b) == 0);
}

431 432 433 434 435 436 437 438 439 440 441 442 443
// 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;
}

444
static void splitRtsFlags(const char *s)
445
{
446 447
    const char *c1, *c2;
    char *t;
448 449 450

    c1 = s;
    do {
Austin Seipp's avatar
Austin Seipp committed
451 452 453
        while (isspace(*c1)) { c1++; };
        c2 = c1;
        while (!isspace(*c2) && *c2 != '\0') { c2++; };
thoughtpolice's avatar
thoughtpolice committed
454

Austin Seipp's avatar
Austin Seipp committed
455
        if (c1 == c2) { break; }
thoughtpolice's avatar
thoughtpolice committed
456

457 458 459
        t = stgMallocBytes(c2-c1+1, "RtsFlags.c:splitRtsFlags()");
        strncpy(t, c1, c2-c1);
        t[c2-c1] = '\0';
460
        appendRtsArg(t);
461

Austin Seipp's avatar
Austin Seipp committed
462
        c1 = c2;
463 464
    } while (*c1 != '\0');
}
thoughtpolice's avatar
thoughtpolice committed
465

466 467
static void errorRtsOptsDisabled(const char *s)
{
468
    char *advice;
469
    if (rtsConfig.rts_hs_main) {
470 471 472 473 474 475 476
        advice = "Link with -rtsopts to enable them.";
    } else {
        advice = "Use hs_init_with_rtsopts() to enable them.";
    }
    errorBelch(s, advice);
}

477 478 479 480 481 482 483
/* -----------------------------------------------------------------------------
   Parse the command line arguments, collecting options for the RTS.

   On return:
     - argv[] is *modified*, any RTS options have been stripped out
     - *argc  contains the new count of arguments in argv[]

484
     - rts_argv[]  (global) contains a copy of the collected RTS args
485 486
     - rts_argc    (global) contains the count of args in rts_argv

487
     - prog_argv[] (global) contains a copy of the non-RTS args (== argv)
488 489
     - prog_argc   (global) contains the count of args in prog_argv

490
     - prog_name   (global) contains the basename of prog_argv[0]
491

492 493
     - rtsConfig   (global) contains the supplied RtsConfig

494 495
  -------------------------------------------------------------------------- */

496
void setupRtsFlags (int *argc, char *argv[], RtsConfig rts_config)
497
{
498 499 500
    nat mode;
    nat total_arg;
    nat arg, rts_argc0;
501

502 503
    rtsConfig = rts_config;

504
    setProgName (argv);
505 506 507
    total_arg = *argc;
    arg = 1;

508
    if (*argc > 1) { *argc = 1; };
509 510
    rts_argc = 0;

511 512
    rts_argv_size = total_arg + 1;
    rts_argv = stgMallocBytes(rts_argv_size * sizeof (char *), "setupRtsFlags");
513

514
    rts_argc0 = rts_argc;
515

516
    // process arguments from the -with-rtsopts compile-time flag first
517 518 519
    // (arguments from the GHCRTS environment variable and the command
    // line override these).
    {
520 521 522 523
        if (rtsConfig.rts_opts != NULL) {
            splitRtsFlags(rtsConfig.rts_opts);
            // opts from rts_opts are always enabled:
            procRtsOpts(rts_argc0, RtsOptsAll);
524 525
            rts_argc0 = rts_argc;
        }
526 527
    }

528
    // process arguments from the GHCRTS environment variable next
529
    // (arguments from the command line override these).
530
    {
Austin Seipp's avatar
Austin Seipp committed
531
        char *ghc_rts = getenv("GHCRTS");
532

Austin Seipp's avatar
Austin Seipp committed
533
        if (ghc_rts != NULL) {
534 535 536
            if (rtsConfig.rts_opts_enabled == RtsOptsNone) {
                errorRtsOptsDisabled(
                    "Warning: Ignoring GHCRTS variable as RTS options are disabled.\n         %s");
537
                // We don't actually exit, just warn
538 539
            } else {
                splitRtsFlags(ghc_rts);
540
                procRtsOpts(rts_argc0, rtsConfig.rts_opts_enabled);
541
                rts_argc0 = rts_argc;
542
            }
543
        }
544
    }
545

546 547 548
    // Split arguments (argv) into PGM (argv) and RTS (rts_argv) parts
    //   argv[0] must be PGM argument -- leave in argv

549
    for (mode = PGM; arg < total_arg; arg++) {
Austin Seipp's avatar
Austin Seipp committed
550 551 552 553 554 555 556 557 558 559 560
        // 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])) {
561 562
            mode = RTS;
        }
Austin Seipp's avatar
Austin Seipp committed
563 564 565
        else if (strequal("-RTS", argv[arg])) {
            mode = PGM;
        }
566
        else if (mode == RTS) {
567
            appendRtsArg(copyArg(argv[arg]));
Ian Lynagh's avatar
Ian Lynagh committed
568
        }
569 570
        else {
            argv[(*argc)++] = argv[arg];
Austin Seipp's avatar
Austin Seipp committed
571
        }
572
    }
573 574
    // process remaining program arguments
    for (; arg < total_arg; arg++) {
Austin Seipp's avatar
Austin Seipp committed
575
        argv[(*argc)++] = argv[arg];
576 577
    }
    argv[*argc] = (char *) 0;
578

579
    procRtsOpts(rts_argc0, rtsConfig.rts_opts_enabled);
580

581
    appendRtsArg((char *)0);
Simon Marlow's avatar
Simon Marlow committed
582
    rts_argc--; // appendRtsArg will have bumped it for the NULL (#7227)
583

584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599
    normaliseRtsOpts();

    setProgArgv(*argc, argv);

    if (RtsFlags.GcFlags.statsFile != NULL) {
        initStatsFile (RtsFlags.GcFlags.statsFile);
    }
    if (RtsFlags.TickyFlags.tickyFile != NULL) {
        initStatsFile (RtsFlags.GcFlags.statsFile);
    }
}

/* -----------------------------------------------------------------------------
 * procRtsOpts: Process rts_argv between rts_argc0 and rts_argc.
 * -------------------------------------------------------------------------- */

600
#if defined(HAVE_UNISTD_H) && defined(HAVE_SYS_TYPES_H) && !defined(mingw32_HOST_OS)
601
static void checkSuid(RtsOptsEnabledEnum enabled)
602 603
{
    if (enabled == RtsOptsSafeOnly) {
Austin Seipp's avatar
Austin Seipp committed
604 605
        /* This doesn't cover linux/posix capabilities like CAP_DAC_OVERRIDE,
           we'd have to link with -lcap for that. */
606
        if ((getuid() != geteuid()) || (getgid() != getegid())) {
607 608
            errorRtsOptsDisabled(
                "RTS options are disabled for setuid binaries. %s");
609 610
            stg_exit(EXIT_FAILURE);
        }
611
    }
612
}
613
#else
614
static void checkSuid (RtsOptsEnabledEnum enabled STG_UNUSED)
615 616 617
{
}
#endif
618

619
static void checkUnsafe(RtsOptsEnabledEnum enabled)
620 621
{
    if (enabled == RtsOptsSafeOnly) {
622
        errorRtsOptsDisabled("Most RTS options are disabled. %s");
623 624 625 626
        stg_exit(EXIT_FAILURE);
    }
}

627 628
static void procRtsOpts (int rts_argc0,
                         RtsOptsEnabledEnum rtsOptsEnabled)
629 630 631
{
    rtsBool error = rtsFalse;
    int arg;
632
    int unchecked_arg_start;
633

634 635
    if (!(rts_argc0 < rts_argc)) return;

636
    if (rtsOptsEnabled == RtsOptsNone) {
637
        errorRtsOptsDisabled("RTS options are disabled. %s");
638 639 640
        stg_exit(EXIT_FAILURE);
    }

641
    checkSuid(rtsOptsEnabled);
642

643
    // Process RTS (rts_argv) part: mainly to determine statsfile
644
    for (arg = rts_argc0; arg < rts_argc; arg++) {
645 646

        /* We handle RtsOptsSafeOnly mode by declaring each option as
Austin Seipp's avatar
Austin Seipp committed
647 648 649
           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. */
650 651 652
        rtsBool option_checked = rtsFalse;

#define OPTION_SAFE option_checked = rtsTrue;
653
#define OPTION_UNSAFE checkUnsafe(rtsOptsEnabled); option_checked = rtsTrue;
654

655
        if (rts_argv[arg][0] != '-') {
Austin Seipp's avatar
Austin Seipp committed
656 657 658
            fflush(stdout);
            errorBelch("unexpected RTS argument: %s", rts_argv[arg]);
            error = rtsTrue;
659 660

        } else {
661 662 663
            /* 0 is dash, 1 is first letter */
            /* see Trac #9839 */
            unchecked_arg_start = 1;
Simon Marlow's avatar
Simon Marlow committed
664
            switch(rts_argv[arg][1]) {
665

Austin Seipp's avatar
Austin Seipp committed
666 667 668 669 670 671
              /* 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.
              */
672 673 674 675 676

#ifdef TICKY_TICKY
# define TICKY_BUILD_ONLY(x) x
#else
# define TICKY_BUILD_ONLY(x) \
677 678
errorBelch("the flag %s requires the program to be built with -ticky", \
           rts_argv[arg]);                                             \
679 680 681 682 683 684 685
error = rtsTrue;
#endif

#ifdef PROFILING
# define PROFILING_BUILD_ONLY(x)   x
#else
# define PROFILING_BUILD_ONLY(x) \
686 687
errorBelch("the flag %s requires the program to be built with -prof", \
           rts_argv[arg]);                                            \
688 689 690
error = rtsTrue;
#endif

691 692
#ifdef TRACING
# define TRACING_BUILD_ONLY(x)   x
Simon Marlow's avatar
Simon Marlow committed
693
#else
694
# define TRACING_BUILD_ONLY(x) \
695 696
errorBelch("the flag %s requires the program to be built with -eventlog or -debug", \
           rts_argv[arg]);                                              \
Simon Marlow's avatar
Simon Marlow committed
697 698 699
error = rtsTrue;
#endif

700 701 702 703
#ifdef THREADED_RTS
# define THREADED_BUILD_ONLY(x)      x
#else
# define THREADED_BUILD_ONLY(x) \
704 705
errorBelch("the flag %s requires the program to be built with -threaded", \
           rts_argv[arg]);                                              \
706 707 708 709 710 711 712
error = rtsTrue;
#endif

#ifdef DEBUG
# define DEBUG_BUILD_ONLY(x) x
#else
# define DEBUG_BUILD_ONLY(x) \
713 714
errorBelch("the flag %s requires the program to be built with -debug", \
           rts_argv[arg]);                                             \
715
error = rtsTrue;
716 717
#endif

Austin Seipp's avatar
Austin Seipp committed
718 719 720 721 722
              /* =========== GENERAL ========================== */
              case '?':
                OPTION_SAFE;
                error = rtsTrue;
                break;
723

724 725 726
              /* This isn't going to allow us to keep related options
                 together as we add more --* flags. We really need a
                 proper options parser. */
Austin Seipp's avatar
Austin Seipp committed
727
              case '-':
728 729
                  if (strequal("install-signal-handlers=yes",
                               &rts_argv[arg][2])) {
730
                      OPTION_UNSAFE;
731 732 733 734
                      RtsFlags.MiscFlags.install_signal_handlers = rtsTrue;
                  }
                  else if (strequal("install-signal-handlers=no",
                               &rts_argv[arg][2])) {
735
                      OPTION_UNSAFE;
736 737
                      RtsFlags.MiscFlags.install_signal_handlers = rtsFalse;
                  }
738 739
                  else if (strequal("machine-readable",
                               &rts_argv[arg][2])) {
740
                      OPTION_UNSAFE;
741 742
                      RtsFlags.MiscFlags.machineReadable = rtsTrue;
                  }
743 744
                  else if (strequal("info",
                               &rts_argv[arg][2])) {
745
                      OPTION_SAFE;
746
                      printRtsInfo();
747
                      stg_exit(0);
748
                  }
749
                  else {
Austin Seipp's avatar
Austin Seipp committed
750 751 752
                      OPTION_SAFE;
                      errorBelch("unknown RTS option: %s",rts_argv[arg]);
                      error = rtsTrue;
753
                  }
Austin Seipp's avatar
Austin Seipp committed
754 755 756
                  break;
              case 'A':
                  OPTION_UNSAFE;
757 758
                  // minimum two blocks in the nursery, so that we have one to
                  // grab for allocate().
759
                  RtsFlags.GcFlags.minAllocAreaSize
760 761 762
                      = decodeSize(rts_argv[arg], 2, 2*BLOCK_SIZE, HS_INT_MAX)
                           / BLOCK_SIZE;
                  break;
763
              case 'n':
764 765 766
                  OPTION_UNSAFE;
                  RtsFlags.GcFlags.nurseryChunkSize
                      = decodeSize(rts_argv[arg], 2, 2*BLOCK_SIZE, HS_INT_MAX)
767 768
                           / BLOCK_SIZE;
                  break;
769

Austin Seipp's avatar
Austin Seipp committed
770 771 772
              case 'B':
                OPTION_UNSAFE;
                RtsFlags.GcFlags.ringBell = rtsTrue;
773
                unchecked_arg_start++;
774
                goto check_rest;
Austin Seipp's avatar
Austin Seipp committed
775 776 777 778 779 780 781 782 783 784

              case 'c':
                  OPTION_UNSAFE;
                  if (rts_argv[arg][2] != '\0') {
                      RtsFlags.GcFlags.compactThreshold =
                          atof(rts_argv[arg]+2);
                  } else {
                      RtsFlags.GcFlags.compact = rtsTrue;
                  }
                  break;
785

786
              case 'w':
Austin Seipp's avatar
Austin Seipp committed
787 788
                OPTION_UNSAFE;
                RtsFlags.GcFlags.sweep = rtsTrue;
789
                unchecked_arg_start++;
790
                goto check_rest;
791

Austin Seipp's avatar
Austin Seipp committed
792 793 794
              case 'F':
                OPTION_UNSAFE;
                RtsFlags.GcFlags.oldGenFactor = atof(rts_argv[arg]+2);
thoughtpolice's avatar
thoughtpolice committed
795

Austin Seipp's avatar
Austin Seipp committed
796 797 798
                if (RtsFlags.GcFlags.oldGenFactor < 0)
                  bad_option( rts_argv[arg] );
                break;
thoughtpolice's avatar
thoughtpolice committed
799

Austin Seipp's avatar
Austin Seipp committed
800
              case 'D':
801
              OPTION_SAFE;
802
              DEBUG_BUILD_ONLY(read_debug_flags(rts_argv[arg]);)
803
              break;
804

Austin Seipp's avatar
Austin Seipp committed
805 806
              case 'K':
                  OPTION_UNSAFE;
807
                  RtsFlags.GcFlags.maxStkSize =
808 809
                      decodeSize(rts_argv[arg], 2, sizeof(W_), HS_WORD_MAX)
                      / sizeof(W_);
810
                  break;
811

Austin Seipp's avatar
Austin Seipp committed
812 813 814
              case 'k':
                OPTION_UNSAFE;
                switch(rts_argv[arg][2]) {
815 816
                case 'c':
                  RtsFlags.GcFlags.stkChunkSize =
817 818
                      decodeSize(rts_argv[arg], 3, sizeof(W_), HS_WORD_MAX)
                      / sizeof(W_);
819 820 821
                  break;
                case 'b':
                  RtsFlags.GcFlags.stkChunkBufferSize =
822 823
                      decodeSize(rts_argv[arg], 3, sizeof(W_), HS_WORD_MAX)
                      / sizeof(W_);
824 825 826
                  break;
                case 'i':
                  RtsFlags.GcFlags.initialStkSize =
827 828
                      decodeSize(rts_argv[arg], 3, sizeof(W_), HS_WORD_MAX)
                      / sizeof(W_);
829 830
                  break;
                default:
831
                  RtsFlags.GcFlags.initialStkSize =
832 833
                      decodeSize(rts_argv[arg], 2, sizeof(W_), HS_WORD_MAX)
                      / sizeof(W_);
834
                  break;
835 836
                }
                break;
837

838
              case 'M':
Austin Seipp's avatar
Austin Seipp committed
839
                  OPTION_UNSAFE;
840
                  RtsFlags.GcFlags.maxHeapSize =
841 842 843 844
                      decodeSize(rts_argv[arg], 2, BLOCK_SIZE, HS_WORD_MAX)
                      / BLOCK_SIZE;
                  /* user give size in *bytes* but "maxHeapSize" is in
                   * *blocks* */
845
                  break;
846

Austin Seipp's avatar
Austin Seipp committed
847 848
              case 'm':
                  OPTION_UNSAFE;
849
                  RtsFlags.GcFlags.pcFreeHeap = atof(rts_argv[arg]+2);
850

851 852 853 854
                  if (RtsFlags.GcFlags.pcFreeHeap < 0 ||
                      RtsFlags.GcFlags.pcFreeHeap > 100)
                      bad_option( rts_argv[arg] );
                  break;
855

Austin Seipp's avatar
Austin Seipp committed
856 857
              case 'G':
                  OPTION_UNSAFE;
858 859 860
                  RtsFlags.GcFlags.generations =
                      decodeSize(rts_argv[arg], 2, 1, HS_INT_MAX);
                  break;
861

Austin Seipp's avatar
Austin Seipp committed
862 863
              case 'H':
                  OPTION_UNSAFE;
864 865 866 867 868 869 870
                  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);
                  }
                  break;
871

872 873 874 875 876 877 878 879
              case 'O':
                  OPTION_UNSAFE;
                  RtsFlags.GcFlags.minOldGenSize =
                      (nat)(decodeSize(rts_argv[arg], 2, BLOCK_SIZE,
                                       HS_WORD_MAX)
                            / BLOCK_SIZE);
                  break;

Austin Seipp's avatar
Austin Seipp committed
880 881 882 883 884
              case 'I': /* idle GC delay */
                OPTION_UNSAFE;
                if (rts_argv[arg][2] == '\0') {
                  /* use default */
                } else {
885 886 887 888 889 890 891
                    Time t = fsecondsToTime(atof(rts_argv[arg]+2));
                    if (t == 0) {
                        RtsFlags.GcFlags.doIdleGC = rtsFalse;
                    } else {
                        RtsFlags.GcFlags.doIdleGC = rtsTrue;
                        RtsFlags.GcFlags.idleGCDelayTime = t;
                    }
Austin Seipp's avatar
Austin Seipp committed
892 893
                }
                break;
894

895
              case 'T':
Austin Seipp's avatar
Austin Seipp committed
896
                  OPTION_SAFE;
897
                  RtsFlags.GcFlags.giveStats = COLLECT_GC_STATS;
898
                  unchecked_arg_start++;
899
                  goto check_rest; /* Don't initialize statistics file. */
900

Austin Seipp's avatar
Austin Seipp committed
901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921
              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;
                    }
922 923
                    r = openStatsFile(rts_argv[arg]+2, NULL,
                                      &RtsFlags.GcFlags.statsFile);
Austin Seipp's avatar
Austin Seipp committed
924 925
                    if (r == -1) { error = rtsTrue; }
                }
Simon Marlow's avatar
Simon Marlow committed
926
                break;
927

Austin Seipp's avatar
Austin Seipp committed
928 929 930
              case 'Z':
                OPTION_UNSAFE;
                RtsFlags.GcFlags.squeezeUpdFrames = rtsFalse;
931
                unchecked_arg_start++;
932
                goto check_rest;
933

Austin Seipp's avatar
Austin Seipp committed
934
              /* =========== PROFILING ========================== */
935

Austin Seipp's avatar
Austin Seipp committed
936 937 938 939 940
              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]) {
941
                  case 'a':
Austin Seipp's avatar
Austin Seipp committed
942
                    RtsFlags.CcFlags.doCostCentres = COST_CENTRES_ALL;
943 944 945 946 947 948
                    if (rts_argv[arg][3] != '\0') {
                      errorBelch("flag -Pa given an argument"
                                 " when none was expected: %s"
                                ,rts_argv[arg]);
                      error = rtsTrue;
                    }
Austin Seipp's avatar
Austin Seipp committed
949
                    break;
950
                  case '\0':
Austin Seipp's avatar
Austin Seipp committed
951 952 953 954 955 956 957 958
                      if (rts_argv[arg][1] == 'P') {
                          RtsFlags.CcFlags.doCostCentres =
                              COST_CENTRES_VERBOSE;
                      } else {
                          RtsFlags.CcFlags.doCostCentres =
                              COST_CENTRES_SUMMARY;
                      }
                      break;
959
                  default:
960
                    unchecked_arg_start++;
961
                    goto check_rest;
Austin Seipp's avatar
Austin Seipp committed
962 963 964 965 966 967
                }
                ) break;

              case 'R':
                  OPTION_SAFE;
                  PROFILING_BUILD_ONLY(
968 969
                      RtsFlags.ProfFlags.maxRetainerSetSize =
                        atof(rts_argv[arg]+2);
Austin Seipp's avatar
Austin Seipp committed
970 971 972 973 974
                  ) break;
              case 'L':
                  OPTION_SAFE;
                  PROFILING_BUILD_ONLY(
                      RtsFlags.ProfFlags.ccsLength = atof(rts_argv[arg]+2);
ravi@bluespec.com's avatar
ravi@bluespec.com committed
975
                      if(RtsFlags.ProfFlags.ccsLength <= 0) {
Austin Seipp's avatar
Austin Seipp committed
976
                        bad_option(rts_argv[arg]);
ravi@bluespec.com's avatar
ravi@bluespec.com committed
977
                      }
Austin Seipp's avatar
Austin Seipp committed
978 979
                  ) break;
              case 'h': /* serial heap profile */
980
#if !defined(PROFILING)
Austin Seipp's avatar
Austin Seipp committed
981 982 983 984 985 986 987 988 989 990
                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;
                }
991
#else
Austin Seipp's avatar
Austin Seipp committed
992 993
                OPTION_SAFE;
                PROFILING_BUILD_ONLY(
994 995
                    error = read_heap_profiling_flag(rts_argv[arg]);
                );
996
#endif /* PROFILING */
Austin Seipp's avatar
Austin Seipp committed
997
                break;
998

Austin Seipp's avatar
Austin Seipp committed
999 1000 1001 1002 1003
              case 'i': /* heap sample interval */
                OPTION_UNSAFE;
                if (rts_argv[arg][2] == '\0') {
                  /* use default */
                } else {
Simon Marlow's avatar
Simon Marlow committed
1004 1005 1006
                    RtsFlags.ProfFlags.heapProfileInterval =
                        fsecondsToTime(atof(rts_argv[arg]+2));
                }