RtsFlags.c 80.8 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"
Simon Marlow's avatar
Simon Marlow committed
18
#include "Capability.h"
19

Ben Gamari's avatar
Ben Gamari committed
20
#if defined(HAVE_CTYPE_H)
21
#include <ctype.h>
22 23
#endif

24 25
#include <string.h>

Ben Gamari's avatar
Ben Gamari committed
26
#if defined(HAVE_UNISTD_H)
27 28 29
#include <unistd.h>
#endif

Ben Gamari's avatar
Ben Gamari committed
30
#if defined(HAVE_SYS_TYPES_H)
31 32 33
#include <sys/types.h>
#endif

34 35
#include <fs_rts.h>

36 37
// Flag Structure
RTS_FLAGS RtsFlags;
38 39 40 41

/*
 * Split argument lists
 */
sof's avatar
sof committed
42
int     prog_argc = 0;    /* an "int" so as to match normal "argc" */
43
char  **prog_argv = NULL;
44 45
int     full_prog_argc = 0;    /* an "int" so as to match normal "argc" */
char  **full_prog_argv = NULL;
sof's avatar
sof committed
46
char   *prog_name = NULL; /* 'basename' of prog_argv[0] */
47
int     rts_argc = 0;  /* ditto */
48
char  **rts_argv = NULL;
49
int     rts_argv_size = 0;
50
#if defined(mingw32_HOST_OS)
51 52 53 54 55
// On Windows hs_main uses GetCommandLineW to get Unicode arguments and
// passes them along UTF8 encoded as argv. We store them here in order to
// free them on exit.
int       win32_full_utf8_argc = 0;
char**    win32_utf8_argv = NULL;
56
#endif
57

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

const RtsConfig defaultRtsConfig  = {
    .rts_opts_enabled = RtsOptsSafeOnly,
Ben Gamari's avatar
Ben Gamari committed
64
    .rts_opts_suggestions = true,
65
    .rts_opts = NULL,
Ben Gamari's avatar
Ben Gamari committed
66 67
    .rts_hs_main = false,
    .keep_cafs = false,
68
    .eventlog_writer = &FileEventLogWriter,
69 70 71 72 73
    .defaultsHook = FlagDefaultsHook,
    .onExitHook = OnExitHook,
    .stackOverflowHook = StackOverflowHook,
    .outOfHeapHook = OutOfHeapHook,
    .mallocFailHook = MallocFailHook,
Simon Marlow's avatar
Simon Marlow committed
74 75 76
    .gcDoneHook = NULL,
    .longGCSync = LongGCSync,
    .longGCSyncEnd = LongGCSyncEnd
77 78
};

79
/*
thoughtpolice's avatar
thoughtpolice committed
80
 * constants, used later
81 82 83 84 85 86 87 88
 */
#define RTS 1
#define PGM 0

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

89
static void procRtsOpts (int rts_argc0, RtsOptsEnabledEnum enabled);
90 91 92

static void normaliseRtsOpts (void);

93
static void initStatsFile (FILE *f);
94

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

98
static StgWord64 decodeSize (
99
    const char *flag, uint32_t offset, StgWord64 min, StgWord64 max);
100

101
static void bad_option (const char *s);
102

Ben Gamari's avatar
Ben Gamari committed
103
#if defined(DEBUG)
104 105 106
static void read_debug_flags(const char *arg);
#endif

Ben Gamari's avatar
Ben Gamari committed
107
#if defined(PROFILING)
Ben Gamari's avatar
Ben Gamari committed
108
static bool read_heap_profiling_flag(const char *arg);
109 110
#endif

Ben Gamari's avatar
Ben Gamari committed
111
#if defined(TRACING)
112
static void read_trace_flags(const char *arg);
113
#endif
114

115
static void errorUsage (void) GNU_ATTRIBUTE(__noreturn__);
116

117 118 119
#if defined(mingw32_HOST_OS)
static char** win32_full_utf8_argv;
#endif
120
static char *  copyArg (char *arg);
121 122
static char ** copyArgv (int argc, char *argv[]);
static void    freeArgv (int argc, char *argv[]);
123
static void setProgName (char *argv[]);
124

125
static void errorRtsOptsDisabled (const char *s);
126

127 128 129 130 131 132
/* -----------------------------------------------------------------------------
 * Command-line option parsing routines.
 * ---------------------------------------------------------------------------*/

void initRtsFlagsDefaults(void)
{
133 134 135
    StgWord64 maxStkSize = 8 * getPhysicalMemorySize() / 10;
    // if getPhysicalMemorySize fails just move along with an 8MB limit
    if (maxStkSize == 0)
136
        maxStkSize = 8 * 1024 * 1024;
137 138 139
    // GcFlags.maxStkSiz is 32-bit, so we need to cap to prevent overflow (#17019)
    else if (maxStkSize > UINT32_MAX * sizeof(W_))
        maxStkSize = UINT32_MAX * sizeof(W_);
140

Austin Seipp's avatar
Austin Seipp committed
141
    RtsFlags.GcFlags.statsFile          = NULL;
Ben Gamari's avatar
Ben Gamari committed
142
    RtsFlags.GcFlags.giveStats          = NO_GC_STATS;
143

144
    RtsFlags.GcFlags.maxStkSize         = maxStkSize / sizeof(W_);
Austin Seipp's avatar
Austin Seipp committed
145
    RtsFlags.GcFlags.initialStkSize     = 1024 / sizeof(W_);
146 147
    RtsFlags.GcFlags.stkChunkSize       = (32 * 1024) / sizeof(W_);
    RtsFlags.GcFlags.stkChunkBufferSize = (1 * 1024) / sizeof(W_);
148

149
    RtsFlags.GcFlags.minAllocAreaSize   = (1024 * 1024)       / BLOCK_SIZE;
Simon Marlow's avatar
Simon Marlow committed
150
    RtsFlags.GcFlags.largeAllocLim      = 0; /* defaults to minAllocAreasize */
151
    RtsFlags.GcFlags.nurseryChunkSize   = 0;
152
    RtsFlags.GcFlags.minOldGenSize      = (1024 * 1024)       / BLOCK_SIZE;
Austin Seipp's avatar
Austin Seipp committed
153
    RtsFlags.GcFlags.maxHeapSize        = 0;    /* off by default */
154
    RtsFlags.GcFlags.heapLimitGrace     = (1024 * 1024);
Austin Seipp's avatar
Austin Seipp committed
155
    RtsFlags.GcFlags.heapSizeSuggestion = 0;    /* none */
Ben Gamari's avatar
Ben Gamari committed
156
    RtsFlags.GcFlags.heapSizeSuggestionAuto = false;
Austin Seipp's avatar
Austin Seipp committed
157
    RtsFlags.GcFlags.pcFreeHeap         = 3;    /* 3% */
158
    RtsFlags.GcFlags.oldGenFactor       = 2;
159
    RtsFlags.GcFlags.useNonmoving       = false;
160
    RtsFlags.GcFlags.nonmovingSelectorOpt = false;
161
    RtsFlags.GcFlags.generations        = 2;
Ben Gamari's avatar
Ben Gamari committed
162 163
    RtsFlags.GcFlags.squeezeUpdFrames   = true;
    RtsFlags.GcFlags.compact            = false;
164
    RtsFlags.GcFlags.compactThreshold   = 30.0;
Ben Gamari's avatar
Ben Gamari committed
165
    RtsFlags.GcFlags.sweep              = false;
Simon Marlow's avatar
Simon Marlow committed
166
    RtsFlags.GcFlags.idleGCDelayTime    = USToTime(300000); // 300ms
Ben Gamari's avatar
Ben Gamari committed
167
#if defined(THREADED_RTS)
Ben Gamari's avatar
Ben Gamari committed
168
    RtsFlags.GcFlags.doIdleGC           = true;
169
#else
Ben Gamari's avatar
Ben Gamari committed
170
    RtsFlags.GcFlags.doIdleGC           = false;
171
#endif
172
    RtsFlags.GcFlags.heapBase           = 0;   /* means don't care */
173
    RtsFlags.GcFlags.allocLimitGrace    = (100*1024) / BLOCK_SIZE;
Ben Gamari's avatar
Ben Gamari committed
174
    RtsFlags.GcFlags.numa               = false;
Simon Marlow's avatar
Simon Marlow committed
175
    RtsFlags.GcFlags.numaMask           = 1;
Ben Gamari's avatar
Ben Gamari committed
176
    RtsFlags.GcFlags.ringBell           = false;
Simon Marlow's avatar
Simon Marlow committed
177
    RtsFlags.GcFlags.longGCSync         = 0; /* detection turned off */
Ben Gamari's avatar
Ben Gamari committed
178 179 180 181 182 183

    RtsFlags.DebugFlags.scheduler       = false;
    RtsFlags.DebugFlags.interpreter     = false;
    RtsFlags.DebugFlags.weak            = false;
    RtsFlags.DebugFlags.gccafs          = false;
    RtsFlags.DebugFlags.gc              = false;
184
    RtsFlags.DebugFlags.nonmoving_gc    = false;
Ben Gamari's avatar
Ben Gamari committed
185 186
    RtsFlags.DebugFlags.block_alloc     = false;
    RtsFlags.DebugFlags.sanity          = false;
Tobias Guggenmos's avatar
Tobias Guggenmos committed
187
    RtsFlags.DebugFlags.zero_on_gc      = false;
Ben Gamari's avatar
Ben Gamari committed
188 189 190 191 192 193 194 195 196
    RtsFlags.DebugFlags.stable          = false;
    RtsFlags.DebugFlags.stm             = false;
    RtsFlags.DebugFlags.prof            = false;
    RtsFlags.DebugFlags.apply           = false;
    RtsFlags.DebugFlags.linker          = false;
    RtsFlags.DebugFlags.squeeze         = false;
    RtsFlags.DebugFlags.hpc             = false;
    RtsFlags.DebugFlags.sparks          = false;
    RtsFlags.DebugFlags.numa            = false;
197
    RtsFlags.DebugFlags.compact         = false;
198

Simon Marlow's avatar
Simon Marlow committed
199
#if defined(PROFILING)
niteria's avatar
niteria committed
200 201
    RtsFlags.CcFlags.doCostCentres      = COST_CENTRES_NONE;
    RtsFlags.CcFlags.outputFileNameStem = NULL;
Simon Marlow's avatar
Simon Marlow committed
202
#endif /* PROFILING */
203

Ben Gamari's avatar
Ben Gamari committed
204
    RtsFlags.ProfFlags.doHeapProfile      = false;
niteria's avatar
niteria committed
205
    RtsFlags.ProfFlags.heapProfileInterval = USToTime(100000); // 100ms
206

Ben Gamari's avatar
Ben Gamari committed
207
#if defined(PROFILING)
Ben Gamari's avatar
Ben Gamari committed
208 209
    RtsFlags.ProfFlags.includeTSOs        = false;
    RtsFlags.ProfFlags.showCCSOnException = false;
210
    RtsFlags.ProfFlags.maxRetainerSetSize = 8;
ravi@bluespec.com's avatar
ravi@bluespec.com committed
211
    RtsFlags.ProfFlags.ccsLength          = 25;
212 213 214 215
    RtsFlags.ProfFlags.modSelector        = NULL;
    RtsFlags.ProfFlags.descrSelector      = NULL;
    RtsFlags.ProfFlags.typeSelector       = NULL;
    RtsFlags.ProfFlags.ccSelector         = NULL;
216
    RtsFlags.ProfFlags.ccsSelector        = NULL;
217 218
    RtsFlags.ProfFlags.retainerSelector   = NULL;
    RtsFlags.ProfFlags.bioSelector        = NULL;
219 220
#endif

Ben Gamari's avatar
Ben Gamari committed
221
#if defined(TRACING)
222
    RtsFlags.TraceFlags.tracing       = TRACE_NONE;
Ben Gamari's avatar
Ben Gamari committed
223 224 225
    RtsFlags.TraceFlags.timestamp     = false;
    RtsFlags.TraceFlags.scheduler     = false;
    RtsFlags.TraceFlags.gc            = false;
226
    RtsFlags.TraceFlags.nonmoving_gc  = false;
Ben Gamari's avatar
Ben Gamari committed
227 228 229
    RtsFlags.TraceFlags.sparks_sampled= false;
    RtsFlags.TraceFlags.sparks_full   = false;
    RtsFlags.TraceFlags.user          = false;
230
    RtsFlags.TraceFlags.trace_output  = NULL;
Simon Marlow's avatar
Simon Marlow committed
231 232
#endif

Ben Gamari's avatar
Ben Gamari committed
233
#if defined(PROFILING)
Simon Marlow's avatar
Simon Marlow committed
234 235 236 237 238 239
    // 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
240

Ben Gamari's avatar
Ben Gamari committed
241
    RtsFlags.MiscFlags.install_signal_handlers = true;
242
    RtsFlags.MiscFlags.install_seh_handlers    = true;
243
    RtsFlags.MiscFlags.generate_stack_trace    = true;
244
    RtsFlags.MiscFlags.generate_dump_file      = false;
245
    RtsFlags.MiscFlags.machineReadable         = false;
246
    RtsFlags.MiscFlags.internalCounters        = false;
247
    RtsFlags.MiscFlags.linkerAlwaysPic         = DEFAULT_LINKER_ALWAYS_PIC;
248
    RtsFlags.MiscFlags.linkerMemBase           = 0;
249

Ben Gamari's avatar
Ben Gamari committed
250
#if defined(THREADED_RTS)
Simon Marlow's avatar
Simon Marlow committed
251
    RtsFlags.ParFlags.nCapabilities     = 1;
Ben Gamari's avatar
Ben Gamari committed
252
    RtsFlags.ParFlags.migrate           = true;
253
    RtsFlags.ParFlags.parGcEnabled      = 1;
254
    RtsFlags.ParFlags.parGcGen          = 0;
Ben Gamari's avatar
Ben Gamari committed
255
    RtsFlags.ParFlags.parGcLoadBalancingEnabled = true;
256
    RtsFlags.ParFlags.parGcLoadBalancingGen = ~0u; /* auto, based on -A */
257
    RtsFlags.ParFlags.parGcNoSyncWithIdle   = 0;
258
    RtsFlags.ParFlags.parGcThreads      = 0; /* defaults to -N */
259
    RtsFlags.ParFlags.setAffinity       = 0;
260
#endif
261

Simon Marlow's avatar
Simon Marlow committed
262
#if defined(THREADED_RTS)
Austin Seipp's avatar
Austin Seipp committed
263
    RtsFlags.ParFlags.maxLocalSparks    = 4096;
Simon Marlow's avatar
Simon Marlow committed
264
#endif /* THREADED_RTS */
265

Ben Gamari's avatar
Ben Gamari committed
266
#if defined(TICKY_TICKY)
Ben Gamari's avatar
Ben Gamari committed
267
    RtsFlags.TickyFlags.showTickyStats   = false;
Austin Seipp's avatar
Austin Seipp committed
268
    RtsFlags.TickyFlags.tickyFile        = NULL;
269 270 271 272 273 274 275 276 277 278 279 280 281 282 283
#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:",
"",
284
"  -?       Prints this message and exits; the program is not executed",
285
"  --info   Print information about the RTS used by this program",
286
"",
Jan Stolarek's avatar
Jan Stolarek committed
287 288
"  -K<size>  Sets the maximum stack size (default: 80% of the heap)",
"            Egs: -K32k -K512k -K8M",
289 290 291
"  -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)",
292
"",
293
"  -A<size>  Sets the minimum allocation area size (default 1m) Egs: -A20m -A10k",
Simon Marlow's avatar
Simon Marlow committed
294 295
"  -AL<size> Sets the amount of large-object memory that can be allocated",
"            before a GC is triggered (default: the value of -A)",
Ömer Sinan Ağacan's avatar
Ömer Sinan Ağacan committed
296 297 298
"  -F<n>     Sets the collecting threshold for old generations as a factor of",
"            the live data in that generation the last time it was collected",
"            (default: 2.0)",
Simon Marlow's avatar
Simon Marlow committed
299 300 301 302
"  -n<size>  Allocation area chunk size (0 = disabled, default: 0)",
"  -O<size>  Sets the minimum size of the old generation (default 1M)",
"  -M<size>  Sets the maximum heap size (default unlimited)  Egs: -M256k -M1G",
"  -H<size>  Sets the minimum heap size (default 0M)   Egs: -H24m  -H1G",
303 304 305
"  -xb<addr> Sets the address from which a suitable start for the heap memory",
"            will be searched from. This is useful if the default address",
"            clashes with some third-party library.",
306
"  -xn       Use the non-moving collector for the old generation.",
Simon Marlow's avatar
Simon Marlow committed
307 308 309
"  -m<n>     Minimum % of heap which must be available (default 3%)",
"  -G<n>     Number of generations (default: 2)",
"  -c<n>     Use in-place compaction instead of copying in the oldest generation",
310 311 312 313
"           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)",
314
"  -w       Use mark-region for the oldest generation (experimental)",
315
#if defined(THREADED_RTS)
316 317
"  -I<sec>  Perform full GC after <sec> idle time (default: 0.3, 0 == off)",
#endif
318
"",
319
"  -T         Collect GC statistics (useful for in-program statistics access)",
320 321 322
"  -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)",
323 324
"",
"",
325 326
"  -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
327
#if defined(PROFILING)
328
"",
329 330 331 332 333 334
"  -p         Time/allocation profile in tree format ",
"             (output file <output prefix>.prof)",
"  -po<file>  Override profiling output file name prefix (program name by default)",
"  -P         More detailed Time/Allocation profile in tree format",
"  -Pa        Give information about *all* cost centres in tree format",
"  -pj        Output cost-center profile in JSON format",
335
"",
336
"  -h         Heap residency profile, by cost centre stack",
337 338 339
"  -h<break-down> Heap residency profile (hp2ps) (output file <program>.hp)",
"     break-down: c = cost centre stack (default)",
"                 m = module",
340
"                 T = closure type",
341 342 343 344
"                 d = closure description",
"                 y = type description",
"                 r = retainer",
"                 b = biography (LAG,DRAG,VOID,USE)",
345
"  A subset of closures may be selected thusly:",
346 347
"    -hc<cc>,...  specific cost centre(s) (top of stack only)",
"    -hC<cc>,...  specific cost centre(s) (anywhere in stack)",
348 349 350 351 352
"    -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)",
353 354
"",
"  -R<size>       Set the maximum retainer set size (default: 8)",
thoughtpolice's avatar
thoughtpolice committed
355
"",
ravi@bluespec.com's avatar
ravi@bluespec.com committed
356 357
"  -L<chars>      Maximum length of a cost-centre stack in a heap profile",
"                 (default: 25)",
358
"",
359 360
"  -xt            Include threads (TSOs) in a heap profile",
"",
361
"  -xc      Show current cost centre stack on raising an exception",
362 363 364
#else /* PROFILING */
"  -h       Heap residency profile (output file <program>.hp)",
"  -hT      Produce a heap profile grouped by closure type",
365
#endif /* PROFILING */
Simon Marlow's avatar
Simon Marlow committed
366

Ben Gamari's avatar
Ben Gamari committed
367
#if defined(TRACING)
Simon Marlow's avatar
Simon Marlow committed
368
"",
369 370
"  -ol<file>  Send binary eventlog to <file> (default: <program>.eventlog)",
"  -l[flags]  Log events to a file",
Ben Gamari's avatar
Ben Gamari committed
371
#  if defined(DEBUG)
372 373 374 375
"  -v[flags]  Log events to stderr",
#  endif
"             where [flags] can contain:",
"                s    scheduler events",
376
"                g    GC and heap events",
377 378
"                p    par spark events (sampled)",
"                f    par spark events (full detail)",
379 380
"                u    user events (emitted from Haskell code)",
"                a    all event classes above",
Ben Gamari's avatar
Ben Gamari committed
381
#  if defined(DEBUG)
382 383
"                t    add time stamps (only useful with -v)",
#  endif
384
"               -x    disable an event class, for any flag above",
385
"             the initial enabled event classes are 'sgpu'",
Simon Marlow's avatar
Simon Marlow committed
386 387
#endif

Simon Marlow's avatar
Simon Marlow committed
388
"  -i<sec>  Time between heap profile samples (seconds, default: 0.1)",
389 390
"",
#if defined(TICKY_TICKY)
391
"  -r<file>  Produce ticky-ticky statistics (with -rstderr for stderr)",
392 393
"",
#endif
394 395 396
"  -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
397 398
"  -V<secs>  Master tick interval in seconds (0 == disable timer).",
"            This sets the resolution for -C and the heap profile timer -i,",
399
"            and is the frequency of time profile samples.",
Ben Gamari's avatar
Ben Gamari committed
400
#if defined(PROFILING)
Simon Marlow's avatar
Simon Marlow committed
401 402 403 404
"            Default: 0.001 sec.",
#else
"            Default: 0.01 sec.",
#endif
405 406 407
"",
#if defined(DEBUG)
"  -Ds  DEBUG: scheduler",
408
"  -Di  DEBUG: interpreter",
409 410 411
"  -Dw  DEBUG: weak",
"  -DG  DEBUG: gccafs",
"  -Dg  DEBUG: gc",
412
"  -Dn  DEBUG: non-moving gc",
413 414
"  -Db  DEBUG: block",
"  -DS  DEBUG: sanity",
Tobias Guggenmos's avatar
Tobias Guggenmos committed
415
"  -DZ  DEBUG: zero freed memory during GC",
416 417
"  -Dt  DEBUG: stable",
"  -Dp  DEBUG: prof",
418
"  -Da  DEBUG: apply",
419
"  -Dl  DEBUG: linker",
420
"  -Dm  DEBUG: stm",
Simon Marlow's avatar
Simon Marlow committed
421
"  -Dz  DEBUG: stack squeezing",
422
"  -Dc  DEBUG: program coverage",
423
"  -Dr  DEBUG: sparks",
424
"  -DC  DEBUG: compact",
425
"",
426
"     NOTE: DEBUG events are sent to stderr by default; add -l to create a",
427
"     binary event log file instead.",
428
"",
429
#endif /* DEBUG */
430
#if defined(THREADED_RTS) && !defined(NOSMP)
431 432
"  -N[<n>]    Use <n> processors (default: 1, -N alone determines",
"             the number of processors to use automatically)",
433
"  -maxN[<n>] Use up to <n> processors automatically",
434 435 436
"  -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>",
Ömer Sinan Ağacan's avatar
Ömer Sinan Ağacan committed
437
"            (default: 1 for -A < 32M, 0 otherwise;",
438
"             -qb alone turns off load-balancing)",
439
"  -qn<n>    Use <n> threads for parallel GC (defaults to value of -N)",
440
"  -qa       Use the OS to set thread affinity (experimental)",
441
"  -qm       Don't automatically migrate threads between CPUs",
442 443 444
"  -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)",
Simon Marlow's avatar
Simon Marlow committed
445 446 447 448 449 450 451 452
"  --numa[=<node_mask>]",
"            Use NUMA, nodes given by <node_mask> (default: off)",
#if defined(DEBUG)
"  --debug-numa[=<num_nodes>]",
"            Pretend NUMA: like --numa, but without the system calls.",
"            Can be used on non-NUMA systems for debugging.",
"",
#endif
453
#endif
454 455
"  --install-signal-handlers=<yes|no>",
"            Install signal handlers (default: yes)",
456 457 458
#if defined(mingw32_HOST_OS)
"  --install-seh-handlers=<yes|no>",
"            Install exception handlers (default: yes)",
459 460 461 462
"  --generate-crash-dumps",
"            Generate Windows crash dumps, requires exception handlers",
"            to be installed. Implies --install-signal-handlers=yes.",
"            (default: no)",
463 464 465 466
"  --generate-stack-traces=<yes|no>",
"            Generate a stack trace when your application encounters a",
"            fatal error. When symbols are available an attempt will be",
"            made to resolve addresses to names. (default: yes)",
467
#endif
Simon Marlow's avatar
Simon Marlow committed
468
#if defined(THREADED_RTS)
469 470
"  -e<n>     Maximum number of outstanding local sparks (default: 4096)",
#endif
471
#if defined(x86_64_HOST_ARCH)
472 473 474 475 476
#if !DEFAULT_LINKER_ALWAYS_PIC
"  -xp       Assume that all object files were compiled with -fPIC",
"            -fexternal-dynamic-refs and load them anywhere in the address",
"            space",
#endif
477 478 479
"  -xm       Base address to mmap memory in the GHCi linker",
"            (hex; must be <80000000)",
#endif
480 481
"  -xq       The allocation limit given to a thread after it receives",
"            an AllocationLimitExceeded exception. (default: 100k)",
482
"",
483 484 485 486 487
"  -Mgrace=<n>",
"            The amount of allocation after the program receives a",
"            HeapOverflow exception before the exception is thrown again, if",
"            the program is still exceeding the heap limit.",
"",
488 489
"RTS options may also be specified using the GHCRTS environment variable.",
"",
490 491 492 493 494 495
"Other RTS options may be available for programs compiled a different way.",
"The GHC User's Guide has full details.",
"",
0
};

496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555
/**
Note [Windows Unicode Arguments]
~~~~~~~~~~~~~~~~~~~~~~~~~~
On Windows argv is usually encoded in the current Codepage which might not
support unicode.

Instead of ignoring the arguments to hs_init we expect them to be utf-8
encoded when coming from a custom main function. In the regular hs_main we
get the unicode arguments from the windows API and pass them along utf8
encoded instead.

This reduces special casing of arguments in later parts of the RTS and base
libraries to dealing with slash differences and using utf8 instead of the
current locale on Windows when decoding arguments.

*/

#if defined(mingw32_HOST_OS)
//Allocate a buffer and return the string utf8 encoded.
char* lpcwstrToUTF8(const wchar_t* utf16_str)
{
    //Check the utf8 encoded size first
    int res = WideCharToMultiByte(CP_UTF8, 0, utf16_str, -1, NULL, 0,
                                  NULL, NULL);
    if (res == 0) {
        return NULL;
    }
    char* buffer = (char*) stgMallocBytes((size_t)res, "getUTF8Args 2");
    res = WideCharToMultiByte(CP_UTF8, 0, utf16_str, -1, buffer, res,
                              NULL, NULL);
    return buffer;
}

char** getUTF8Args(int* argc)
{
    LPCWSTR cmdLine = GetCommandLineW();
    LPWSTR* argvw = CommandLineToArgvW(cmdLine, argc);

    // We create two argument arrays, one which is later permutated by the RTS
    // instead of the main argv.
    // The other one is used to free the allocted memory later.
    char** argv = (char**) stgMallocBytes(sizeof(char*) * (*argc + 1),
                                          "getUTF8Args 1");
    win32_full_utf8_argv = (char**) stgMallocBytes(sizeof(char*) * (*argc + 1),
                                                   "getUTF8Args 1");

    for (int i = 0; i < *argc; i++)
    {
        argv[i] = lpcwstrToUTF8(argvw[i]);
    }
    argv[*argc] = NULL;
    memcpy(win32_full_utf8_argv, argv, sizeof(char*) * (*argc + 1));

    LocalFree(argvw);
    win32_utf8_argv = argv;
    win32_full_utf8_argc = *argc;
    return argv;
}
#endif

Ben Gamari's avatar
Ben Gamari committed
556
STATIC_INLINE bool strequal(const char *a, const char * b)
557 558 559 560
{
    return(strcmp(a, b) == 0);
}

561 562 563 564 565 566 567 568 569 570 571 572 573
// 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;
}

574
static void splitRtsFlags(const char *s)
575
{
576 577
    const char *c1, *c2;
    char *t;
578 579 580

    c1 = s;
    do {
Austin Seipp's avatar
Austin Seipp committed
581 582 583
        while (isspace(*c1)) { c1++; };
        c2 = c1;
        while (!isspace(*c2) && *c2 != '\0') { c2++; };
thoughtpolice's avatar
thoughtpolice committed
584

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

587 588 589
        t = stgMallocBytes(c2-c1+1, "RtsFlags.c:splitRtsFlags()");
        strncpy(t, c1, c2-c1);
        t[c2-c1] = '\0';
590
        appendRtsArg(t);
591

Austin Seipp's avatar
Austin Seipp committed
592
        c1 = c2;
593 594
    } while (*c1 != '\0');
}
thoughtpolice's avatar
thoughtpolice committed
595

596 597
static void errorRtsOptsDisabled(const char *s)
{
598
    char *advice;
599
    if (rtsConfig.rts_hs_main) {
600 601 602 603 604 605 606
        advice = "Link with -rtsopts to enable them.";
    } else {
        advice = "Use hs_init_with_rtsopts() to enable them.";
    }
    errorBelch(s, advice);
}

607 608 609 610 611 612 613
/* -----------------------------------------------------------------------------
   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[]

614
     - rts_argv[]  (global) contains a copy of the collected RTS args
615 616
     - rts_argc    (global) contains the count of args in rts_argv

617
     - prog_argv[] (global) contains a copy of the non-RTS args (== argv)
618 619
     - prog_argc   (global) contains the count of args in prog_argv

620
     - prog_name   (global) contains the basename of prog_argv[0]
621

622 623
     - rtsConfig   (global) contains the supplied RtsConfig

624 625
  On Windows argv is assumed to be utf8 encoded for unicode compatibility.
  See Note [Windows Unicode Arguments]
626

627 628
  -------------------------------------------------------------------------- */

629
void setupRtsFlags (int *argc, char *argv[], RtsConfig rts_config)
630
{
631 632 633
    uint32_t mode;
    uint32_t total_arg;
    uint32_t arg, rts_argc0;
634

635 636
    rtsConfig = rts_config;

637
    setProgName (argv);
638 639 640
    total_arg = *argc;
    arg = 1;

641
    if (*argc > 1) { *argc = 1; };
642 643
    rts_argc = 0;

644 645
    rts_argv_size = total_arg + 1;
    rts_argv = stgMallocBytes(rts_argv_size * sizeof (char *), "setupRtsFlags");
646

647
    rts_argc0 = rts_argc;
648

649
    // process arguments from the -with-rtsopts compile-time flag first
650 651 652
    // (arguments from the GHCRTS environment variable and the command
    // line override these).
    {
653 654 655 656
        if (rtsConfig.rts_opts != NULL) {
            splitRtsFlags(rtsConfig.rts_opts);
            // opts from rts_opts are always enabled:
            procRtsOpts(rts_argc0, RtsOptsAll);
657 658
            rts_argc0 = rts_argc;
        }
659 660
    }

661
    // process arguments from the GHCRTS environment variable next
662
    // (arguments from the command line override these).
663 664
    // If we ignore all non-builtin rtsOpts we skip these.
    if(rtsConfig.rts_opts_enabled != RtsOptsIgnoreAll)
665
    {
Austin Seipp's avatar
Austin Seipp committed
666
        char *ghc_rts = getenv("GHCRTS");
667

Austin Seipp's avatar
Austin Seipp committed
668
        if (ghc_rts != NULL) {
669 670 671
            if (rtsConfig.rts_opts_enabled == RtsOptsNone) {
                errorRtsOptsDisabled(
                    "Warning: Ignoring GHCRTS variable as RTS options are disabled.\n         %s");
672
                // We don't actually exit, just warn
673 674
            } else {
                splitRtsFlags(ghc_rts);
675
                procRtsOpts(rts_argc0, rtsConfig.rts_opts_enabled);
676
                rts_argc0 = rts_argc;
677
            }
678
        }
679
    }
680

681

682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714
    // If we ignore all commandline rtsOpts we skip processing of argv by
    // the RTS completely
    if(!(rtsConfig.rts_opts_enabled == RtsOptsIgnoreAll ||
         rtsConfig.rts_opts_enabled == RtsOptsIgnore)
    )
    {
        // Split arguments (argv) into PGM (argv) and RTS (rts_argv) parts
        //   argv[0] must be PGM argument -- leave in argv
        //
        for (mode = PGM; arg < total_arg; arg++) {
            // The '--RTS' argument disables all future
            // +RTS ... -RTS processing.
            if (strequal("--RTS", argv[arg])) {
                arg++;
                break;
            }
            // The '--' argument is passed through to the program, but
            // disables all further +RTS ... -RTS processing.
            else if (strequal("--", argv[arg])) {
                break;
            }
            else if (strequal("+RTS", argv[arg])) {
                mode = RTS;
            }
            else if (strequal("-RTS", argv[arg])) {
                mode = PGM;
            }
            else if (mode == RTS) {
                appendRtsArg(copyArg(argv[arg]));
            }
            else {
                argv[(*argc)++] = argv[arg];
            }
Austin Seipp's avatar
Austin Seipp committed
715
        }
716

717
    }
718

719 720
    // process remaining program arguments
    for (; arg < total_arg; arg++) {
Austin Seipp's avatar
Austin Seipp committed
721
        argv[(*argc)++] = argv[arg];
722 723
    }
    argv[*argc] = (char *) 0;
724

725
    procRtsOpts(rts_argc0, rtsConfig.rts_opts_enabled);
726

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

730 731 732 733 734 735 736
    normaliseRtsOpts();

    setProgArgv(*argc, argv);

    if (RtsFlags.GcFlags.statsFile != NULL) {
        initStatsFile (RtsFlags.GcFlags.statsFile);
    }
Ben Gamari's avatar
Ben Gamari committed
737
#if defined(TICKY_TICKY)
738
    if (RtsFlags.TickyFlags.tickyFile != NULL) {
739
        initStatsFile (RtsFlags.TickyFlags.tickyFile);
740
    }
741
#endif
742 743 744 745 746 747
}

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

748
#if defined(HAVE_UNISTD_H) && defined(HAVE_SYS_TYPES_H) && !defined(mingw32_HOST_OS)
749
static void checkSuid(RtsOptsEnabledEnum enabled)
750 751
{
    if (enabled == RtsOptsSafeOnly) {
Austin Seipp's avatar
Austin Seipp committed
752 753
        /* This doesn't cover linux/posix capabilities like CAP_DAC_OVERRIDE,
           we'd have to link with -lcap for that. */
754
        if ((getuid() != geteuid()) || (getgid() != getegid())) {
755 756
            errorRtsOptsDisabled(
                "RTS options are disabled for setuid binaries. %s");
757 758
            stg_exit(EXIT_FAILURE);
        }
759
    }
760
}
761
#else
762
static void checkSuid (RtsOptsEnabledEnum enabled STG_UNUSED)
763 764 765
{
}
#endif
766

767
static void checkUnsafe(RtsOptsEnabledEnum enabled)
768 769
{
    if (enabled == RtsOptsSafeOnly) {
770
        errorRtsOptsDisabled("Most RTS options are disabled. %s");
771 772 773 774
        stg_exit(EXIT_FAILURE);
    }
}

775 776
static void procRtsOpts (int rts_argc0,
                         RtsOptsEnabledEnum rtsOptsEnabled)
777
{
Ben Gamari's avatar
Ben Gamari committed
778
    bool error = false;
779
    int arg;
780
    int unchecked_arg_start;
781

782 783
    if (!(rts_argc0 < rts_argc)) return;

784
    if (rtsOptsEnabled == RtsOptsNone) {
785
        errorRtsOptsDisabled("RTS options are disabled. %s");
786 787 788
        stg_exit(EXIT_FAILURE);
    }

789
    checkSuid(rtsOptsEnabled);
790

791
    // Process RTS (rts_argv) part: mainly to determine statsfile
792
    for (arg = rts_argc0; arg < rts_argc; arg++) {
793 794

        /* We handle RtsOptsSafeOnly mode by declaring each option as
Austin Seipp's avatar
Austin Seipp committed
795 796 797
           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. */
Ben Gamari's avatar
Ben Gamari committed
798
        bool option_checked = false;
799

800
// See Note [OPTION_SAFE vs OPTION_UNSAFE].
Ben Gamari's avatar
Ben Gamari committed
801 802
#define OPTION_SAFE option_checked = true;
#define OPTION_UNSAFE checkUnsafe(rtsOptsEnabled); option_checked = true;
803

804
        if (rts_argv[arg][0] != '-') {
Austin Seipp's avatar
Austin Seipp committed
805 806
            fflush(stdout);
            errorBelch("unexpected RTS argument: %s", rts_argv[arg]);
Ben Gamari's avatar
Ben Gamari committed
807
            error = true;
808 809

        } else {
810
            /* 0 is dash, 1 is first letter */
811
            /* see #9839 */
812
            unchecked_arg_start = 1;
Simon Marlow's avatar
Simon Marlow committed
813
            switch(rts_argv[arg][1]) {
814

Austin Seipp's avatar
Austin Seipp committed
815 816 817 818 819 820
              /* 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.
              */
821

Ben Gamari's avatar
Ben Gamari committed
822
#if defined(TICKY_TICKY)
823 824 825
# define TICKY_BUILD_ONLY(x) x
#else
# define TICKY_BUILD_ONLY(x) \
826 827
errorBelch("the flag %s requires the program to be built with -ticky", \
           rts_argv[arg]);                                             \
Ben Gamari's avatar
Ben Gamari committed
828
error = true;
829 830
#endif

Ben Gamari's avatar
Ben Gamari committed
831
#if defined(PROFILING)
832 833 834
# define PROFILING_BUILD_ONLY(x)   x
#else
# define PROFILING_BUILD_ONLY(x) \
835 836
errorBelch("the flag %s requires the program to be built with -prof", \
           rts_argv[arg]);                                            \
Ben Gamari's avatar
Ben Gamari committed
837
error = true;
838 839
#endif

Ben Gamari's avatar
Ben Gamari committed
840
#if defined(TRACING)
841
# define TRACING_BUILD_ONLY(x)   x
Simon Marlow's avatar
Simon Marlow committed
842
#else
843
# define TRACING_BUILD_ONLY(x) \
844
errorBelch("the flag %s requires the program to be built with -eventlog, -prof or -debug", \
845
           rts_argv[arg]);                                              \
Ben Gamari's avatar
Ben Gamari committed
846
error = true;
Simon Marlow's avatar
Simon Marlow committed
847 848
#endif

Ben Gamari's avatar
Ben Gamari committed
849
#if defined(THREADED_RTS)
850 851 852
# define THREADED_BUILD_ONLY(x)      x
#else
# define THREADED_BUILD_ONLY(x) \
853 854
errorBelch("the flag %s requires the program to be built with -threaded", \
           rts_argv[arg]);                                              \
Ben Gamari's avatar
Ben Gamari committed
855
error = true;
856 857
#endif

Ben Gamari's avatar
Ben Gamari committed
858
#if defined(DEBUG)
859 860 861
# define DEBUG_BUILD_ONLY(x) x
#else
# define DEBUG_BUILD_ONLY(x) \
862 863
errorBelch("the flag %s requires the program to be built with -debug", \
           rts_argv[arg]);                                             \
Ben Gamari's avatar
Ben Gamari committed
864
error = true;
865 866
#endif

Austin Seipp's avatar
Austin Seipp committed
867 868 869
              /* =========== GENERAL ========================== */
              case '?':
                OPTION_SAFE;
Ben Gamari's avatar
Ben Gamari committed
870
                error = true;
Austin Seipp's avatar
Austin Seipp committed
871
                break;
872

873 874 875
              /* 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
876
              case '-':
877 878
                  if (strequal("install-signal-handlers=yes",
                               &rts_argv[arg][2])) {
879
                      OPTION_UNSAFE;
Ben Gamari's avatar
Ben Gamari committed
880
                      RtsFlags.MiscFlags.install_signal_handlers = true;
881 882 883
                  }
                  else if (strequal("install-signal-handlers=no",
                               &rts_argv[arg][2])) {
884
                      OPTION_UNSAFE;
Ben Gamari's avatar
Ben Gamari committed
885
                      RtsFlags.MiscFlags.install_signal_handlers = false;
886
                  }
887 888 889 890 891 892 893 894 895 896
                  else if (strequal("install-seh-handlers=yes",
                              &rts_argv[arg][2])) {
                      OPTION_UNSAFE;
                      RtsFlags.MiscFlags.install_seh_handlers = true;
                  }
                  else if (strequal("install-seh-handlers=no",
                              &rts_argv[arg][2])) {
                      OPTION_UNSAFE;
                      RtsFlags.MiscFlags.install_seh_handlers = false;
                  }
897 898 899 900 901 902 903 904 905 906
                  else if (strequal("generate-stack-traces=yes",
                               &rts_argv[arg][2])) {
                      OPTION_UNSAFE;
                      RtsFlags.MiscFlags.generate_stack_trace = true;
                  }
                  else if (strequal("generate-stack-traces=no",
                              &rts_argv[arg][2])) {
                      OPTION_UNSAFE;
                      RtsFlags.MiscFlags.generate_stack_trace = false;
                  }
907 908 909 910 911
                  else if (strequal("generate-crash-dumps",
                              &rts_argv[arg][2])) {
                      OPTION_UNSAFE;
                      RtsFlags.MiscFlags.generate_dump_file = true;
                  }
912 913
                  else if (strequal("machine-readable",
                               &rts_argv[arg][2])) {
914
                      OPTION_UNSAFE;
Ben Gamari's avatar
Ben Gamari committed
915
                      RtsFlags.MiscFlags.machineReadable = true;
916
                  }
917 918 919 920 921
                  else if (strequal("internal-counters",
                                    &rts_argv[arg][2])) {
                      OPTION_SAFE;
                      RtsFlags.MiscFlags.internalCounters = true;
                  }
922 923
                  else if (strequal("info",
                               &rts_argv[arg][2])) {
924
                      OPTION_SAFE;
925
                      printRtsInfo(rtsConfig);
926
                      stg_exit(0);
927
                  }
Simon Marlow's avatar
Simon Marlow committed
928 929
#if defined(THREADED_RTS)
                  else if (!strncmp("numa", &rts_argv[arg][2], 4)) {
930 931 932 933 934 935
                      if (!osBuiltWithNumaSupport()) {
                          errorBelch("%s: This GHC build was compiled without NUMA support.",
                                     rts_argv[arg]);
                          error = true;
                          break;
                      }
Simon Marlow's avatar
Simon Marlow committed
936 937 938 939 940 941 942 943 944 945 946
                      OPTION_SAFE;
                      StgWord mask;
                      if (rts_argv[arg][6] == '=') {
                          mask = (StgWord)strtol(rts_argv[arg]+7,
                                                 (char **) NULL, 10);
                      } else {
                          mask = (StgWord)~0;
                      }
                      if (!osNumaAvailable()) {
                          errorBelch("%s: OS reports NUMA is not available",
                                     rts_argv[arg]);
Ben Gamari's avatar
Ben Gamari committed
947
                          error = true;
Simon Marlow's avatar
Simon Marlow committed
948 949 950
                          break;
                      }

Ben Gamari's avatar
Ben Gamari committed
951
                      RtsFlags.GcFlags.numa = true;
Simon Marlow's avatar
Simon Marlow committed
952
                      RtsFlags.GcFlags.numaMask = mask;
Simon Marlow's avatar
Simon Marlow committed
953 954 955 956 957 958 959 960 961 962 963 964 965
                  }
#endif
#if defined(DEBUG) && defined(THREADED_RTS)
                  else if (!strncmp("debug-numa", &rts_argv[arg][2], 10)) {
                      OPTION_SAFE;
                      size_t nNodes;
                      if (rts_argv[arg][12] == '=' &&
                          isdigit(rts_argv[arg][13])) {
                          nNodes = (StgWord)strtol(rts_argv[arg]+13,
                                                 (char **) NULL, 10);
                      } else {
                          errorBelch("%s: missing number of nodes",
                                     rts_argv[arg]);
Ben Gamari's avatar
Ben Gamari committed
966
                          error = true;
Simon Marlow's avatar
Simon Marlow committed
967 968 969 970 971
                          break;
                      }
                      if (nNodes > MAX_NUMA_NODES) {
                          errorBelch("%s: Too many NUMA nodes (max %d)",
                                     rts_argv[arg], MAX_NUMA_NODES);
Ben Gamari's avatar
Ben Gamari committed
972
                          error = true;
Simon Marlow's avatar
Simon Marlow committed
973
                      } else {
Ben Gamari's avatar
Ben Gamari committed
974 975
                          RtsFlags.GcFlags.numa = true;
                          RtsFlags.DebugFlags.numa = true;
Simon Marlow's avatar
Simon Marlow committed
976
                          RtsFlags.GcFlags.numaMask = (1<<nNodes) - 1;
Simon Marlow's avatar
Simon Marlow committed
977 978 979
                      }
                  }
#endif
Simon Marlow's avatar
Simon Marlow committed
980 981 982 983 984 985 986 987 988 989
                  else if (!strncmp("long-gc-sync=", &rts_argv[arg][2], 13)) {
                      OPTION_SAFE;
                      if (rts_argv[arg][2] == '\0') {
                          /* use default */
                      } else {
                          RtsFlags.GcFlags.longGCSync =
                              fsecondsToTime(atof(rts_argv[arg]+16));
                      }
                      break;
                  }
990
                  else {
Austin Seipp's avatar
Austin Seipp committed
991 992
                      OPTION_SAFE;
                      errorBelch("unknown RTS option: %s",rts_argv[arg]);