RtsFlags.c 53.9 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 "RtsUtils.h"
14
#include "Profiling.h"
15
#include "RtsFlags.h"
16

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

21 22
#include <string.h>

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

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

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

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

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

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

63 64 65 66 67 68 69 70 71 72 73 74 75
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);
76

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

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

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

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

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

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

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

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

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

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

152
    RtsFlags.ProfFlags.doHeapProfile      = rtsFalse;
Simon Marlow's avatar
Simon Marlow committed
153
    RtsFlags.ProfFlags. heapProfileInterval = USToTime(100000); // 100ms
154 155

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

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

Simon Marlow's avatar
Simon Marlow committed
179 180 181 182 183 184 185
#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
186

187
    RtsFlags.MiscFlags.install_signal_handlers = rtsTrue;
Ian Lynagh's avatar
Ian Lynagh committed
188
    RtsFlags.MiscFlags.machineReadable = rtsFalse;
189
    RtsFlags.MiscFlags.linkerMemBase    = 0;
190

191
#ifdef THREADED_RTS
192
    RtsFlags.ParFlags.nNodes	        = 1;
193
    RtsFlags.ParFlags.migrate           = rtsTrue;
194
    RtsFlags.ParFlags.parGcEnabled      = 1;
195 196 197
    RtsFlags.ParFlags.parGcGen          = 0;
    RtsFlags.ParFlags.parGcLoadBalancingEnabled = rtsTrue;
    RtsFlags.ParFlags.parGcLoadBalancingGen = 1;
198
    RtsFlags.ParFlags.setAffinity       = 0;
199
#endif
200

Simon Marlow's avatar
Simon Marlow committed
201
#if defined(THREADED_RTS)
202
    RtsFlags.ParFlags.maxLocalSparks	= 4096;
Simon Marlow's avatar
Simon Marlow committed
203
#endif /* THREADED_RTS */
204 205

#ifdef TICKY_TICKY
206 207
    RtsFlags.TickyFlags.showTickyStats	 = rtsFalse;
    RtsFlags.TickyFlags.tickyFile	 = NULL;
208
#endif
Simon Marlow's avatar
Simon Marlow committed
209

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

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

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

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

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

408
static void splitRtsFlags(const char *s)
409
{
410 411
    const char *c1, *c2;
    char *t;
412 413 414 415 416 417 418 419 420

    c1 = s;
    do {
	while (isspace(*c1)) { c1++; };
	c2 = c1;
	while (!isspace(*c2) && *c2 != '\0') { c2++; };
	
	if (c1 == c2) { break; }
	
421 422 423 424
        t = stgMallocBytes(c2-c1+1, "RtsFlags.c:splitRtsFlags()");
        strncpy(t, c1, c2-c1);
        t[c2-c1] = '\0';
        rts_argv[rts_argc++] = t;
425

426 427 428 429
	c1 = c2;
    } while (*c1 != '\0');
}
    
430 431 432 433 434 435 436
/* -----------------------------------------------------------------------------
   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[]

437
     - rts_argv[]  (global) contains a copy of the collected RTS args
438 439
     - rts_argc    (global) contains the count of args in rts_argv

440
     - prog_argv[] (global) contains a copy of the non-RTS args (== argv)
441 442
     - prog_argc   (global) contains the count of args in prog_argv

443
     - prog_name   (global) contains the basename of prog_argv[0]
444 445 446

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

447 448 449
void setupRtsFlags (int *argc, char *argv[],
                    RtsOptsEnabledEnum rtsOptsEnabled,
                    const char *ghc_rts_opts)
450
{
451 452 453
    nat mode;
    nat total_arg;
    nat arg, rts_argc0;
454

455
    setProgName (argv);
456 457 458 459
    total_arg = *argc;
    arg = 1;

    *argc = 1;
460 461
    rts_argc = 0;

462 463
    rts_argv = stgCallocBytes(total_arg + 1, sizeof (char *), "setupRtsFlags");

464
    rts_argc0 = rts_argc;
465

466 467 468 469 470
    // 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) {
471 472 473 474 475
            splitRtsFlags(ghc_rts_opts);
            // opts from ghc_rts_opts are always enabled:
            procRtsOpts(rts_argc0, RtsOptsAll);
            rts_argc0 = rts_argc;
        }
476 477
    }

478
    // process arguments from the GHCRTS environment variable next
479
    // (arguments from the command line override these).
480 481 482 483
    {
	char *ghc_rts = getenv("GHCRTS");

	if (ghc_rts != NULL) {
484
            if (rtsOptsEnabled == RtsOptsNone) {
485
                errorBelch("Warning: Ignoring GHCRTS variable as RTS options are disabled.\n         Link with -rtsopts to enable them.");
486
                // We don't actually exit, just warn
487 488 489 490
            } else {
                splitRtsFlags(ghc_rts);
                procRtsOpts(rts_argc0, rtsOptsEnabled);
                rts_argc0 = rts_argc;
491
            }
492
        }
493
    }
494

495 496 497
    // Split arguments (argv) into PGM (argv) and RTS (rts_argv) parts
    //   argv[0] must be PGM argument -- leave in argv

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

547 548 549
static void checkSuid(RtsOptsEnabledEnum enabled)
{
    if (enabled == RtsOptsSafeOnly) {
550
#if defined(HAVE_UNISTD_H) && defined(HAVE_SYS_TYPES_H) && !defined(mingw32_HOST_OS)
551 552 553 554 555 556 557
	/* 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
558
    }
559 560 561 562 563 564 565 566 567 568
}

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

569
static void procRtsOpts (int rts_argc0, RtsOptsEnabledEnum rtsOptsEnabled)
570 571 572
{
    rtsBool error = rtsFalse;
    int arg;
573

574 575
    if (!(rts_argc0 < rts_argc)) return;

576
    if (rtsOptsEnabled == RtsOptsNone) {
577 578 579 580 581 582
        errorBelch("RTS options are disabled. Link with -rtsopts to enable them.");
        stg_exit(EXIT_FAILURE);
    }

    checkSuid(rtsOptsEnabled);

583
    // Process RTS (rts_argv) part: mainly to determine statsfile
584
    for (arg = rts_argc0; arg < rts_argc; arg++) {
585 586 587 588 589 590 591 592

        /* 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;
593
#define OPTION_UNSAFE checkUnsafe(rtsOptsEnabled); option_checked = rtsTrue;
594

595
        if (rts_argv[arg][0] != '-') {
596
	    fflush(stdout);
597
	    errorBelch("unexpected RTS argument: %s", rts_argv[arg]);
598 599 600
	    error = rtsTrue;

        } else {
Simon Marlow's avatar
Simon Marlow committed
601 602

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

Simon Marlow's avatar
Simon Marlow committed
604 605 606 607 608
	      /* 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.
609 610 611 612 613 614
	      */

#ifdef TICKY_TICKY
# define TICKY_BUILD_ONLY(x) x
#else
# define TICKY_BUILD_ONLY(x) \
615
errorBelch("the flag %s requires the program to be built with -ticky", rts_argv[arg]); \
616 617 618 619 620 621 622
error = rtsTrue;
#endif

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

627 628
#ifdef TRACING
# define TRACING_BUILD_ONLY(x)   x
Simon Marlow's avatar
Simon Marlow committed
629
#else
630
# define TRACING_BUILD_ONLY(x) \
631
errorBelch("the flag %s requires the program to be built with -eventlog or -debug", rts_argv[arg]); \
Simon Marlow's avatar
Simon Marlow committed
632 633 634
error = rtsTrue;
#endif

635 636 637 638
#ifdef THREADED_RTS
# define THREADED_BUILD_ONLY(x)      x
#else
# define THREADED_BUILD_ONLY(x) \
639 640 641 642 643 644 645 646 647
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]); \
648
error = rtsTrue;
649 650 651 652
#endif

	      /* =========== GENERAL ========================== */
	      case '?':
653
		OPTION_SAFE;
654 655 656
		error = rtsTrue;
		break;

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

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

734
	      case 'B':
735
        	OPTION_UNSAFE;
736 737 738
		RtsFlags.GcFlags.ringBell = rtsTrue;
		break;

739
	      case 'c':
740
        	  OPTION_UNSAFE;
741 742 743 744
		  if (rts_argv[arg][2] != '\0') {
		      RtsFlags.GcFlags.compactThreshold =
			  atof(rts_argv[arg]+2);
		  } else {
745
		      RtsFlags.GcFlags.compact = rtsTrue;
746 747
		  }
		  break;
748

749
              case 'w':
750
        	OPTION_UNSAFE;
751 752 753
		RtsFlags.GcFlags.sweep = rtsTrue;
		break;

754
	      case 'F':
755
        	OPTION_UNSAFE;
756 757 758 759 760 761
	        RtsFlags.GcFlags.oldGenFactor = atof(rts_argv[arg]+2);
	      
		if (RtsFlags.GcFlags.oldGenFactor < 0)
		  bad_option( rts_argv[arg] );
		break;
	      
762
	      case 'D':
763
              OPTION_SAFE;
764
              DEBUG_BUILD_ONLY(
765 766 767 768 769 770 771 772
	      { 
		  char *c;

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

	      case 'K':
826
        	  OPTION_UNSAFE;
827
                  RtsFlags.GcFlags.maxStkSize =
828
                      decodeSize(rts_argv[arg], 2, sizeof(W_), HS_WORD_MAX) / sizeof(W_);
829
                  break;
830 831

	      case 'k':
832
        	OPTION_UNSAFE;
833 834 835 836 837 838 839 840 841 842 843 844 845 846
		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:
847
                  RtsFlags.GcFlags.initialStkSize =
848
                      decodeSize(rts_argv[arg], 2, sizeof(W_), HS_WORD_MAX) / sizeof(W_);
849
                  break;
850 851
                }
                break;
852

853
              case 'M':
854
        	  OPTION_UNSAFE;
855 856 857 858
                  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;
859 860

	      case 'm':
861
        	  OPTION_UNSAFE;
862
                  RtsFlags.GcFlags.pcFreeHeap = atof(rts_argv[arg]+2);
863

864 865 866 867
                  if (RtsFlags.GcFlags.pcFreeHeap < 0 ||
                      RtsFlags.GcFlags.pcFreeHeap > 100)
                      bad_option( rts_argv[arg] );
                  break;
868

869
	      case 'G':
870
        	  OPTION_UNSAFE;
871 872 873
                  RtsFlags.GcFlags.generations =
                      decodeSize(rts_argv[arg], 2, 1, HS_INT_MAX);
                  break;
874

875
	      case 'H':
876
        	  OPTION_UNSAFE;
877 878 879 880 881 882 883
                  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;
884

885 886
#ifdef RTS_GTK_FRONTPANEL
	      case 'f':
887
        	  OPTION_UNSAFE;
888 889 890 891
		  RtsFlags.GcFlags.frontpanel = rtsTrue;
		  break;
#endif

892
    	      case 'I':	/* idle GC delay */
893
        	OPTION_UNSAFE;
894 895 896
		if (rts_argv[arg][2] == '\0') {
		  /* use default */
		} else {
Simon Marlow's avatar
Simon Marlow committed
897 898
                    RtsFlags.GcFlags.idleGCDelayTime =
                        fsecondsToTime(atof(rts_argv[arg]+2));
899 900 901
		}
		break;

902
              case 'T':
903
        	  OPTION_SAFE;
904 905 906
                  RtsFlags.GcFlags.giveStats = COLLECT_GC_STATS;
                  break; /* Don't initialize statistics file. */

907
	      case 'S':
908
		  OPTION_SAFE; /* but see below */
909 910
		  RtsFlags.GcFlags.giveStats = VERBOSE_GC_STATS;
		  goto stats;
911

912
	      case 's':
913
        	  OPTION_SAFE; /* but see below */
914 915
		  RtsFlags.GcFlags.giveStats = SUMMARY_GC_STATS;
		  goto stats;
916 917

	      case 't':
918
        	  OPTION_SAFE; /* but see below */
919 920
		  RtsFlags.GcFlags.giveStats = ONELINE_GC_STATS;
		  goto stats;
921

922
	    stats:
923 924
		{ 
		    int r;
925 926 927
		    if (rts_argv[arg][2] != '\0') {
		      OPTION_UNSAFE;
		    }
928 929
                    r = openStatsFile(rts_argv[arg]+2, NULL,
                                      &RtsFlags.GcFlags.statsFile);
930 931
		    if (r == -1) { error = rtsTrue; }
		}
Simon Marlow's avatar