RtsFlags.c 54 KB
Newer Older
1
/* -----------------------------------------------------------------------------
2 3
 *
 * (c) The AQUA Project, Glasgow University, 1994-1997
Simon Marlow's avatar
Simon Marlow committed
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 "RtsOpts.h"
14
#include "RtsUtils.h"
15
#include "Profiling.h"
16
#include "RtsFlags.h"
17

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

22 23
#include <string.h>

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

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

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

/*
 * Split argument lists
 */
sof's avatar
sof committed
38
int     prog_argc = 0;    /* an "int" so as to match normal "argc" */
39
char  **prog_argv = NULL;
40 41
int     full_prog_argc = 0;    /* an "int" so as to match normal "argc" */
char  **full_prog_argv = NULL;
sof's avatar
sof committed
42
char   *prog_name = NULL; /* 'basename' of prog_argv[0] */
43
int     rts_argc = 0;  /* ditto */
44
char  **rts_argv = NULL;
45 46 47 48 49 50 51 52
#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
53 54 55 56 57 58 59 60 61 62 63

/*
 * constants, used later 
 */
#define RTS 1
#define PGM 0

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

64 65 66 67 68 69 70 71 72 73 74 75 76
static void procRtsOpts      (int rts_argc0, RtsOptsEnabledEnum enabled);

static void normaliseRtsOpts (void);

static void initStatsFile    (FILE *f);

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

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

static void bad_option       (const char *s);
77

78 79 80
#ifdef TRACING
static void read_trace_flags(char *arg);
#endif
81

82 83
static void errorUsage      (void) GNU_ATTRIBUTE(__noreturn__);

84 85 86 87
static char *  copyArg  (char *arg);
static char ** copyArgv (int argc, char *argv[]);
static void    freeArgv (int argc, char *argv[]);

88 89 90 91 92 93 94 95 96
/* -----------------------------------------------------------------------------
 * Command-line option parsing routines.
 * ---------------------------------------------------------------------------*/

void initRtsFlagsDefaults(void)
{
    RtsFlags.GcFlags.statsFile		= NULL;
    RtsFlags.GcFlags.giveStats		= NO_GC_STATS;

97
    RtsFlags.GcFlags.maxStkSize		= (8 * 1024 * 1024) / sizeof(W_);
98
    RtsFlags.GcFlags.initialStkSize	= 1024 / sizeof(W_);
99 100
    RtsFlags.GcFlags.stkChunkSize       = (32 * 1024) / sizeof(W_);
    RtsFlags.GcFlags.stkChunkBufferSize = (1 * 1024) / sizeof(W_);
101

102
    RtsFlags.GcFlags.minAllocAreaSize   = (512 * 1024)        / BLOCK_SIZE;
103
    RtsFlags.GcFlags.minOldGenSize      = (1024 * 1024)       / BLOCK_SIZE;
104
    RtsFlags.GcFlags.maxHeapSize	= 0;    /* off by default */
105
    RtsFlags.GcFlags.heapSizeSuggestion	= 0;    /* none */
106
    RtsFlags.GcFlags.heapSizeSuggestionAuto = rtsFalse;
107
    RtsFlags.GcFlags.pcFreeHeap		= 3;	/* 3% */
108
    RtsFlags.GcFlags.oldGenFactor       = 2;
109
    RtsFlags.GcFlags.generations        = 2;
110
    RtsFlags.GcFlags.squeezeUpdFrames	= rtsTrue;
111
    RtsFlags.GcFlags.compact            = rtsFalse;
112
    RtsFlags.GcFlags.compactThreshold   = 30.0;
113
    RtsFlags.GcFlags.sweep              = rtsFalse;
114 115 116
#ifdef RTS_GTK_FRONTPANEL
    RtsFlags.GcFlags.frontpanel         = rtsFalse;
#endif
117
    RtsFlags.GcFlags.idleGCDelayTime    = 300; /* millisecs */
118

119 120 121 122 123 124 125 126 127 128 129 130
#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

131 132
#ifdef DEBUG
    RtsFlags.DebugFlags.scheduler	= rtsFalse;
133
    RtsFlags.DebugFlags.interpreter	= rtsFalse;
134 135 136 137 138 139
    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;
140
    RtsFlags.DebugFlags.stm             = rtsFalse;
141
    RtsFlags.DebugFlags.prof		= rtsFalse;
142
    RtsFlags.DebugFlags.apply		= rtsFalse;
143
    RtsFlags.DebugFlags.linker		= rtsFalse;
144
    RtsFlags.DebugFlags.squeeze		= rtsFalse;
145
    RtsFlags.DebugFlags.hpc		= rtsFalse;
146
    RtsFlags.DebugFlags.sparks		= rtsFalse;
147 148
#endif

Simon Marlow's avatar
Simon Marlow committed
149
#if defined(PROFILING)
150
    RtsFlags.CcFlags.doCostCentres	= 0;
Simon Marlow's avatar
Simon Marlow committed
151
#endif /* PROFILING */
152

153
    RtsFlags.ProfFlags.doHeapProfile      = rtsFalse;
154
    RtsFlags.ProfFlags.profileInterval    = 100;
155 156

#ifdef PROFILING
157
    RtsFlags.ProfFlags.includeTSOs        = rtsFalse;
158
    RtsFlags.ProfFlags.showCCSOnException = rtsFalse;
159
    RtsFlags.ProfFlags.maxRetainerSetSize = 8;
ravi@bluespec.com's avatar
ravi@bluespec.com committed
160
    RtsFlags.ProfFlags.ccsLength          = 25;
161 162 163 164
    RtsFlags.ProfFlags.modSelector        = NULL;
    RtsFlags.ProfFlags.descrSelector      = NULL;
    RtsFlags.ProfFlags.typeSelector       = NULL;
    RtsFlags.ProfFlags.ccSelector         = NULL;
165
    RtsFlags.ProfFlags.ccsSelector        = NULL;
166 167
    RtsFlags.ProfFlags.retainerSelector   = NULL;
    RtsFlags.ProfFlags.bioSelector        = NULL;
168 169
#endif

170
#ifdef TRACING
171
    RtsFlags.TraceFlags.tracing       = TRACE_NONE;
172 173
    RtsFlags.TraceFlags.timestamp     = rtsFalse;
    RtsFlags.TraceFlags.scheduler     = rtsFalse;
174
    RtsFlags.TraceFlags.gc            = rtsFalse;
175 176
    RtsFlags.TraceFlags.sparks_sampled= rtsFalse;
    RtsFlags.TraceFlags.sparks_full   = rtsFalse;
177
    RtsFlags.TraceFlags.user          = rtsFalse;
Simon Marlow's avatar
Simon Marlow committed
178 179
#endif

180 181
    RtsFlags.MiscFlags.tickInterval	= 20;  /* In milliseconds */
    RtsFlags.ConcFlags.ctxtSwitchTime	= 20;  /* In milliseconds */
182

183
    RtsFlags.MiscFlags.install_signal_handlers = rtsTrue;
Ian Lynagh's avatar
Ian Lynagh committed
184
    RtsFlags.MiscFlags.machineReadable = rtsFalse;
185
    RtsFlags.MiscFlags.linkerMemBase    = 0;
186

187
#ifdef THREADED_RTS
188
    RtsFlags.ParFlags.nNodes	        = 1;
189
    RtsFlags.ParFlags.migrate           = rtsTrue;
190
    RtsFlags.ParFlags.parGcEnabled      = 1;
191 192 193
    RtsFlags.ParFlags.parGcGen          = 0;
    RtsFlags.ParFlags.parGcLoadBalancingEnabled = rtsTrue;
    RtsFlags.ParFlags.parGcLoadBalancingGen = 1;
194
    RtsFlags.ParFlags.setAffinity       = 0;
195
#endif
196

Simon Marlow's avatar
Simon Marlow committed
197
#if defined(THREADED_RTS)
198
    RtsFlags.ParFlags.maxLocalSparks	= 4096;
Simon Marlow's avatar
Simon Marlow committed
199
#endif /* THREADED_RTS */
200 201

#ifdef TICKY_TICKY
202 203
    RtsFlags.TickyFlags.showTickyStats	 = rtsFalse;
    RtsFlags.TickyFlags.tickyFile	 = NULL;
204
#endif
Simon Marlow's avatar
Simon Marlow committed
205

206
#ifdef USE_PAPI
207 208
    /* By default no special measurements taken */
    RtsFlags.PapiFlags.eventType        = 0;
209
    RtsFlags.PapiFlags.numUserEvents    = 0;
210
#endif
211 212 213 214 215 216 217 218 219 220 221 222 223 224
}

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:",
"",
225
"  -?       Prints this message and exits; the program is not executed",
226
"  --info   Print information about the RTS used by this program",
227
"",
228
"  -K<size> Sets the maximum stack size (default 8M)  Egs: -K32k   -K512k",
229 230 231
"  -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)",
232
"",
233
"  -A<size> Sets the minimum allocation area size (default 512k) Egs: -A1m -A10k",
234
"  -M<size> Sets the maximum heap size (default unlimited)  Egs: -M256k -M1G",
235
"  -H<size> Sets the minimum heap size (default 0M)   Egs: -H24m  -H1G",
236
"  -m<n>    Minimum % of heap which must be available (default 3%)",
237
"  -G<n>    Number of generations (default: 2)",
238 239 240 241 242
"  -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)",
243
"  -w       Use mark-region for the oldest generation (experimental)",
244
#if defined(THREADED_RTS)
245 246
"  -I<sec>  Perform full GC after <sec> idle time (default: 0.3, 0 == off)",
#endif
247
"",
248
"  -T         Collect GC statistics (useful for in-program statistics access)"
249 250 251
"  -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)",
252 253 254
#ifdef RTS_GTK_FRONTPANEL
"  -f       Display front panel (requires X11 & GTK+)",
#endif
255 256 257 258
"",
"",
"  -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
259
#if defined(PROFILING)
260
"",
261
"  -px      Time/allocation profile (XML)  (output file <program>.prof)",
andy's avatar
andy committed
262 263 264
"  -p       Time/allocation profile        (output file <program>.prof)",
"  -P       More detailed Time/Allocation profile",
"  -Pa      Give information about *all* cost centres",
265

266 267
# if defined(PROFILING)
"",
268 269 270 271 272 273 274 275
"  -hx            Heap residency profile (XML)   (output file <program>.prof)",
"  -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)",
276
"  A subset of closures may be selected thusly:",
277 278
"    -hc<cc>,...  specific cost centre(s) (top of stack only)",
"    -hC<cc>,...  specific cost centre(s) (anywhere in stack)",
279 280 281 282 283
"    -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)",
284 285
"",
"  -R<size>       Set the maximum retainer set size (default: 8)",
ravi@bluespec.com's avatar
ravi@bluespec.com committed
286 287 288
"", 
"  -L<chars>      Maximum length of a cost-centre stack in a heap profile",
"                 (default: 25)",
289
"",
290 291
"  -xt            Include threads (TSOs) in a heap profile",
"",
292
"  -xc      Show current cost centre stack on raising an exception",
293 294
# endif
#endif /* PROFILING or PAR */
Simon Marlow's avatar
Simon Marlow committed
295

296
#ifdef TRACING
Simon Marlow's avatar
Simon Marlow committed
297
"",
298 299 300 301 302 303
"  -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",
304
"                g    GC events",
305 306
"                p    par spark events (sampled)",
"                f    par spark events (full detail)",
307 308
"                u    user events (emitted from Haskell code)",
"                a    all event classes above",
309 310 311
#  ifdef DEBUG
"                t    add time stamps (only useful with -v)",
#  endif
312
"               -x    disable an event class, for any flag above",
313
"             the initial enabled event classes are 'sgpu'",
Simon Marlow's avatar
Simon Marlow committed
314 315
#endif

316
#if !defined(PROFILING)
317
"",
318
"  -hT      Heap residency profile (output file <program>.hp)",
319
#endif
320
"  -i<sec>  Time between heap samples (seconds, default: 0.1)",
321 322
"",
#if defined(TICKY_TICKY)
323
"  -r<file>  Produce ticky-ticky statistics (with -rstderr for stderr)",
324 325
"",
#endif
326 327 328
"  -C<secs>  Context-switch interval in seconds.",
"            0 or no argument means switch as often as possible.",
"            Default: 0.02 sec; resolution is set by -V below.",
329
"  -V<secs>  Master tick interval in seconds (0 == disable timer).",
330 331
"            This sets the resolution for -C and the profile timer -i.",
"            Default: 0.02 sec.",
332 333 334
"",
#if defined(DEBUG)
"  -Ds  DEBUG: scheduler",
335
"  -Di  DEBUG: interpreter",
336 337 338 339 340 341 342
"  -Dw  DEBUG: weak",
"  -DG  DEBUG: gccafs",
"  -Dg  DEBUG: gc",
"  -Db  DEBUG: block",
"  -DS  DEBUG: sanity",
"  -Dt  DEBUG: stable",
"  -Dp  DEBUG: prof",
343
"  -Da  DEBUG: apply",
344
"  -Dl  DEBUG: linker",
345
"  -Dm  DEBUG: stm",
Simon Marlow's avatar
Simon Marlow committed
346
"  -Dz  DEBUG: stack squeezing",
347
"  -Dc  DEBUG: program coverage",
348 349
"  -Dr  DEBUG: sparks",
"",
350
"     NOTE: DEBUG events are sent to stderr by default; add -l to create a",
351
"     binary event log file instead.",
352
"",
353
#endif /* DEBUG */
354
#if defined(THREADED_RTS) && !defined(NOSMP)
355 356
"  -N<n>     Use <n> processors (default: 1)",
"  -N        Determine the number of processors to use automatically",
357 358 359 360 361
"  -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)",
362
"  -qm       Don't automatically migrate threads between CPUs",
363
#endif
364 365
"  --install-signal-handlers=<yes|no>",
"            Install signal handlers (default: yes)",
Simon Marlow's avatar
Simon Marlow committed
366
#if defined(THREADED_RTS)
367 368
"  -e<n>     Maximum number of outstanding local sparks (default: 4096)",
#endif
369 370 371 372
#if defined(x86_64_HOST_ARCH)
"  -xm       Base address to mmap memory in the GHCi linker",
"            (hex; must be <80000000)",
#endif
373
#if defined(USE_PAPI)
374 375
"  -aX       CPU performance counter measurements using PAPI",
"            (use with the -s<file> option).  X is one of:",
376 377 378 379 380 381
"",
/* "            y - cycles", */
"            1 - level 1 cache misses",
"            2 - level 2 cache misses",
"            b - branch mispredictions",
"            s - stalled cycles",
382
"            e - cache miss and branch misprediction events",
383 384
"            +PAPI_EVENT   - collect papi preset event PAPI_EVENT",
"            #NATIVE_EVENT - collect native event NATIVE_EVENT (in hex)",
385
#endif
386
"",
387 388
"RTS options may also be specified using the GHCRTS environment variable.",
"",
389 390 391 392 393 394
"Other RTS options may be available for programs compiled a different way.",
"The GHC User's Guide has full details.",
"",
0
};

sof's avatar
sof committed
395
STATIC_INLINE rtsBool
396 397 398 399 400
strequal(const char *a, const char * b)
{
    return(strcmp(a, b) == 0);
}

401
static void splitRtsFlags(char *s)
402 403 404 405 406 407 408 409 410 411 412
{
    char *c1, *c2;

    c1 = s;
    do {
	while (isspace(*c1)) { c1++; };
	c2 = c1;
	while (!isspace(*c2) && *c2 != '\0') { c2++; };
	
	if (c1 == c2) { break; }
	
413 414 415 416 417
        s = stgMallocBytes(c2-c1+1, "RtsFlags.c:splitRtsFlags()");
        strncpy(s, c1, c2-c1);
        s[c2-c1] = '\0';
        rts_argv[rts_argc++] = s;

418 419 420 421
	c1 = c2;
    } while (*c1 != '\0');
}
    
422 423 424 425 426 427 428
/* -----------------------------------------------------------------------------
   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[]

429
     - rts_argv[]  (global) contains a copy of the collected RTS args
430 431
     - rts_argc    (global) contains the count of args in rts_argv

432
     - prog_argv[] (global) contains a copy of the non-RTS args (== argv)
433 434
     - prog_argc   (global) contains the count of args in prog_argv

435
     - prog_name   (global) contains the basename of prog_argv[0]
436 437 438 439

  -------------------------------------------------------------------------- */

void setupRtsFlags (int *argc, char *argv[])
440
{
441 442 443
    nat mode;
    nat total_arg;
    nat arg, rts_argc0;
444

445
    setProgName (argv);
446 447 448 449
    total_arg = *argc;
    arg = 1;

    *argc = 1;
450 451
    rts_argc = 0;

452 453
    rts_argv = stgCallocBytes(total_arg + 1, sizeof (char *), "setupRtsFlags");

454
    rts_argc0 = rts_argc;
455

456 457 458 459 460
    // process arguments from the ghc_rts_opts global variable first.
    // (arguments from the GHCRTS environment variable and the command
    // line override these).
    {
	if (ghc_rts_opts != NULL) {
461 462 463 464 465
            splitRtsFlags(ghc_rts_opts);
            // opts from ghc_rts_opts are always enabled:
            procRtsOpts(rts_argc0, RtsOptsAll);
            rts_argc0 = rts_argc;
        }
466 467
    }

468
    // process arguments from the GHCRTS environment variable next
469
    // (arguments from the command line override these).
470 471 472 473
    {
	char *ghc_rts = getenv("GHCRTS");

	if (ghc_rts != NULL) {
474
            if (rtsOptsEnabled == RtsOptsNone) {
475
                errorBelch("Warning: Ignoring GHCRTS variable as RTS options are disabled.\n         Link with -rtsopts to enable them.");
476
                // We don't actually exit, just warn
477 478 479 480
            } else {
                splitRtsFlags(ghc_rts);
                procRtsOpts(rts_argc0, rtsOptsEnabled);
                rts_argc0 = rts_argc;
481
            }
482
        }
483
    }
484

485 486 487
    // Split arguments (argv) into PGM (argv) and RTS (rts_argv) parts
    //   argv[0] must be PGM argument -- leave in argv

488 489 490 491 492 493 494 495 496 497 498 499
    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])) {
500 501
            mode = RTS;
        }
502 503 504
	else if (strequal("-RTS", argv[arg])) {
	    mode = PGM;
	}
505 506
        else if (mode == RTS) {
            rts_argv[rts_argc++] = copyArg(argv[arg]);
Ian Lynagh's avatar
Ian Lynagh committed
507
        }
508 509
        else {
            argv[(*argc)++] = argv[arg];
510 511
	}
    }
512 513 514
    // process remaining program arguments
    for (; arg < total_arg; arg++) {
	argv[(*argc)++] = argv[arg];
515 516
    }
    argv[*argc] = (char *) 0;
517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536
    rts_argv[rts_argc] = (char *) 0;

    procRtsOpts(rts_argc0, rtsOptsEnabled);

    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.
 * -------------------------------------------------------------------------- */

537 538
static void checkSuid(RtsOptsEnabledEnum enabled)
{
539
#if defined(HAVE_UNISTD_H) && defined(HAVE_SYS_TYPES_H) && !defined(mingw32_HOST_OS)
540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558
    if (enabled == RtsOptsSafeOnly) {
	/* 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())) {
            errorBelch("RTS options are disabled for setuid binaries. Link with -rtsopts to enable them.");
            stg_exit(EXIT_FAILURE);
        }
    }
#endif
}

static void checkUnsafe(RtsOptsEnabledEnum enabled)
{
    if (enabled == RtsOptsSafeOnly) {
        errorBelch("Most RTS options are disabled. Link with -rtsopts to enable them.");
        stg_exit(EXIT_FAILURE);
    }
}

559 560 561 562
static void procRtsOpts (int rts_argc0, RtsOptsEnabledEnum enabled)
{
    rtsBool error = rtsFalse;
    int arg;
563

564 565 566 567 568 569 570 571 572
    if (!(rts_argc0 < rts_argc)) return;

    if (enabled == RtsOptsNone) {
        errorBelch("RTS options are disabled. Link with -rtsopts to enable them.");
        stg_exit(EXIT_FAILURE);
    }

    checkSuid(rtsOptsEnabled);

573
    // Process RTS (rts_argv) part: mainly to determine statsfile
574
    for (arg = rts_argc0; arg < rts_argc; arg++) {
575 576 577 578 579 580 581 582 583 584

        /* 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. */
        rtsBool option_checked = rtsFalse;

#define OPTION_SAFE option_checked = rtsTrue;
#define OPTION_UNSAFE checkUnsafe(enabled); option_checked = rtsTrue;

585
        if (rts_argv[arg][0] != '-') {
586
	    fflush(stdout);
587
	    errorBelch("unexpected RTS argument: %s", rts_argv[arg]);
588 589 590
	    error = rtsTrue;

        } else {
Simon Marlow's avatar
Simon Marlow committed
591 592

            switch(rts_argv[arg][1]) {
593

Simon Marlow's avatar
Simon Marlow committed
594 595 596 597 598
	      /* 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.
599 600 601 602 603 604
	      */

#ifdef TICKY_TICKY
# define TICKY_BUILD_ONLY(x) x
#else
# define TICKY_BUILD_ONLY(x) \
605
errorBelch("the flag %s requires the program to be built with -ticky", rts_argv[arg]); \
606 607 608 609 610 611 612
error = rtsTrue;
#endif

#ifdef PROFILING
# define PROFILING_BUILD_ONLY(x)   x
#else
# define PROFILING_BUILD_ONLY(x) \
613
errorBelch("the flag %s requires the program to be built with -prof", rts_argv[arg]); \
614 615 616
error = rtsTrue;
#endif

617 618
#ifdef TRACING
# define TRACING_BUILD_ONLY(x)   x
Simon Marlow's avatar
Simon Marlow committed
619
#else
620
# define TRACING_BUILD_ONLY(x) \
621
errorBelch("the flag %s requires the program to be built with -eventlog or -debug", rts_argv[arg]); \
Simon Marlow's avatar
Simon Marlow committed
622 623 624
error = rtsTrue;
#endif

625 626 627 628
#ifdef THREADED_RTS
# define THREADED_BUILD_ONLY(x)      x
#else
# define THREADED_BUILD_ONLY(x) \
629 630 631 632 633 634 635 636 637
errorBelch("the flag %s requires the program to be built with -threaded", rts_argv[arg]); \
error = rtsTrue;
#endif

#ifdef DEBUG
# 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]); \
638
error = rtsTrue;
639 640 641 642
#endif

	      /* =========== GENERAL ========================== */
	      case '?':
643
		OPTION_SAFE;
644 645 646
		error = rtsTrue;
		break;

647 648 649 650 651 652
              /* 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 '-':
                  if (strequal("install-signal-handlers=yes",
                               &rts_argv[arg][2])) {
653
                      OPTION_UNSAFE;
654 655 656 657
                      RtsFlags.MiscFlags.install_signal_handlers = rtsTrue;
                  }
                  else if (strequal("install-signal-handlers=no",
                               &rts_argv[arg][2])) {
658
                      OPTION_UNSAFE;
659 660
                      RtsFlags.MiscFlags.install_signal_handlers = rtsFalse;
                  }
Ian Lynagh's avatar
Ian Lynagh committed
661 662
                  else if (strequal("machine-readable",
                               &rts_argv[arg][2])) {
663
                      OPTION_UNSAFE;
Ian Lynagh's avatar
Ian Lynagh committed
664 665
                      RtsFlags.MiscFlags.machineReadable = rtsTrue;
                  }
666 667
                  else if (strequal("info",
                               &rts_argv[arg][2])) {
668
                      OPTION_SAFE;
669
                      printRtsInfo();
670
                      stg_exit(0);
671
                  }
672
                  else {
673
		      OPTION_SAFE;
674 675 676 677
		      errorBelch("unknown RTS option: %s",rts_argv[arg]);
		      error = rtsTrue;
                  }
		  break;
678
	      case 'A':
679
        	  OPTION_UNSAFE;
680 681 682 683
                  RtsFlags.GcFlags.minAllocAreaSize
                      = decodeSize(rts_argv[arg], 2, BLOCK_SIZE, HS_INT_MAX)
                           / BLOCK_SIZE;
                  break;
684

685 686
#ifdef USE_PAPI
	      case 'a':
687
        	OPTION_UNSAFE;
688 689 690 691 692 693 694 695 696 697 698 699 700
		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;
701 702 703
		case 'e':
		  RtsFlags.PapiFlags.eventType = PAPI_FLAG_CB_EVENTS;
		  break;
704
                case '+':
705
                case '#':
706 707 708 709
                  if (RtsFlags.PapiFlags.numUserEvents >= MAX_PAPI_USER_EVENTS) {
                      errorBelch("maximum number of PAPI events reached");
                      stg_exit(EXIT_FAILURE);
                  }
710 711 712 713 714
                  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;
715
                  RtsFlags.PapiFlags.eventType = PAPI_USER_EVENTS;
716
                  RtsFlags.PapiFlags.userEventsKind[eventNum] = eventKind;
717
                  break;
718 719 720 721 722 723
		default:
		  bad_option( rts_argv[arg] );
		}
		break;
#endif

724
	      case 'B':
725
        	OPTION_UNSAFE;
726 727 728
		RtsFlags.GcFlags.ringBell = rtsTrue;
		break;

729
	      case 'c':
730
        	  OPTION_UNSAFE;
731 732 733 734
		  if (rts_argv[arg][2] != '\0') {
		      RtsFlags.GcFlags.compactThreshold =
			  atof(rts_argv[arg]+2);
		  } else {
735
		      RtsFlags.GcFlags.compact = rtsTrue;
736 737
		  }
		  break;
738

739
              case 'w':
740
        	OPTION_UNSAFE;
741 742 743
		RtsFlags.GcFlags.sweep = rtsTrue;
		break;

744
	      case 'F':
745
        	OPTION_UNSAFE;
746 747 748 749 750 751
	        RtsFlags.GcFlags.oldGenFactor = atof(rts_argv[arg]+2);
	      
		if (RtsFlags.GcFlags.oldGenFactor < 0)
		  bad_option( rts_argv[arg] );
		break;
	      
752
	      case 'D':
753
              OPTION_SAFE;
754
              DEBUG_BUILD_ONLY(
755 756 757 758 759 760 761 762
	      { 
		  char *c;

		  for (c  = rts_argv[arg] + 2; *c != '\0'; c++) {
		      switch (*c) {
		      case 's':
			  RtsFlags.DebugFlags.scheduler = rtsTrue;
			  break;
763 764
		      case 'i':
			  RtsFlags.DebugFlags.interpreter = rtsTrue;
765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789
			  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;
790 791 792
		      case 'a':
			  RtsFlags.DebugFlags.apply = rtsTrue;
			  break;
793 794 795
		      case 'm':
			  RtsFlags.DebugFlags.stm = rtsTrue;
			  break;
796 797 798
		      case 'z':
			  RtsFlags.DebugFlags.squeeze = rtsTrue;
			  break;
799 800 801
		      case 'c':
			  RtsFlags.DebugFlags.hpc = rtsTrue;
			  break;
802 803 804
		      case 'r':
			  RtsFlags.DebugFlags.sparks = rtsTrue;
			  break;
805 806 807 808
		      default:
			  bad_option( rts_argv[arg] );
		      }
		  }
809 810
                  // -Dx also turns on -v.  Use -l to direct trace
                  // events to the .eventlog file instead.
811
                  RtsFlags.TraceFlags.tracing = TRACE_STDERR;
812 813
	      })
              break;
814 815

	      case 'K':
816
        	  OPTION_UNSAFE;
817
                  RtsFlags.GcFlags.maxStkSize =
818
                      decodeSize(rts_argv[arg], 2, sizeof(W_), HS_WORD_MAX) / sizeof(W_);
819
                  break;
820 821

	      case 'k':
822
        	OPTION_UNSAFE;
823 824 825 826 827 828 829 830 831 832 833 834 835 836
		switch(rts_argv[arg][2]) {
                case 'c':
                  RtsFlags.GcFlags.stkChunkSize =
                      decodeSize(rts_argv[arg], 3, sizeof(W_), HS_WORD_MAX) / sizeof(W_);
                  break;
                case 'b':
                  RtsFlags.GcFlags.stkChunkBufferSize =
                      decodeSize(rts_argv[arg], 3, sizeof(W_), HS_WORD_MAX) / sizeof(W_);
                  break;
                case 'i':
                  RtsFlags.GcFlags.initialStkSize =
                      decodeSize(rts_argv[arg], 3, sizeof(W_), HS_WORD_MAX) / sizeof(W_);
                  break;
                default:
837
                  RtsFlags.GcFlags.initialStkSize =
838
                      decodeSize(rts_argv[arg], 2, sizeof(W_), HS_WORD_MAX) / sizeof(W_);
839
                  break;
840 841
                }
                break;
842

843
              case 'M':
844
        	  OPTION_UNSAFE;
845 846 847 848
                  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* */
                  break;
849 850

	      case 'm':
851
        	  OPTION_UNSAFE;
852
                  RtsFlags.GcFlags.pcFreeHeap = atof(rts_argv[arg]+2);
853

854 855 856 857
                  if (RtsFlags.GcFlags.pcFreeHeap < 0 ||
                      RtsFlags.GcFlags.pcFreeHeap > 100)
                      bad_option( rts_argv[arg] );
                  break;
858

859
	      case 'G':
860
        	  OPTION_UNSAFE;
861 862 863
                  RtsFlags.GcFlags.generations =
                      decodeSize(rts_argv[arg], 2, 1, HS_INT_MAX);
                  break;
864

865
	      case 'H':
866
        	  OPTION_UNSAFE;
867 868 869 870 871 872 873
                  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;
874

875 876
#ifdef RTS_GTK_FRONTPANEL
	      case 'f':
877
        	  OPTION_UNSAFE;
878 879 880 881
		  RtsFlags.GcFlags.frontpanel = rtsTrue;
		  break;
#endif

882
    	      case 'I':	/* idle GC delay */
883
        	OPTION_UNSAFE;
884 885 886 887 888
		if (rts_argv[arg][2] == '\0') {
		  /* use default */
		} else {
		    I_ cst; /* tmp */

889
		    /* Convert to millisecs */
890
		    cst = (I_) ((atof(rts_argv[arg]+2) * 1000));
891
		    RtsFlags.GcFlags.idleGCDelayTime = cst;
892 893 894
		}
		break;

895
              case 'T':
896
        	  OPTION_SAFE;
897 898 899
                  RtsFlags.GcFlags.giveStats = COLLECT_GC_STATS;
                  break; /* Don't initialize statistics file. */

900
	      case 'S':
901
		  OPTION_SAFE; /* but see below */
902 903
		  RtsFlags.GcFlags.giveStats = VERBOSE_GC_STATS;
		  goto stats;
904

905
	      case 's':
906
        	  OPTION_SAFE; /* but see below */
907 908
		  RtsFlags.GcFlags.giveStats = SUMMARY_GC_STATS;
		  goto stats;
909 910

	      case 't':
911
        	  OPTION_SAFE; /* but see below */
912 913
		  RtsFlags.GcFlags.giveStats = ONELINE_GC_STATS;
		  goto stats;
914

915
	    stats:
916 917
		{ 
		    int r;
918 919 920
		    if (rts_argv[arg][2] != '\0') {
		      OPTION_UNSAFE;
		    }
921 922
                    r = openStatsFile(rts_argv[arg]+2, NULL,
                                      &RtsFlags.GcFlags.statsFile);
923 924
		    if (r == -1) { error = rtsTrue; }
		}
Simon Marlow's avatar
Simon Marlow committed
925
                break;
926 927

	      case 'Z':
928
		OPTION_UNSAFE;
929 930 931 932 933 934 935
		RtsFlags.GcFlags.squeezeUpdFrames = rtsFalse;
		break;

	      /* =========== PROFILING ========================== */

	      case 'P': /* detailed cost centre profiling (time/alloc) */
	      case 'p': /* cost centre profiling (time/alloc) */
936
		OPTION_SAFE;