Add +RTS -AL<size>
[ghc.git] / rts / RtsFlags.c
1 /* -----------------------------------------------------------------------------
2 *
3 * (c) The AQUA Project, Glasgow University, 1994-1997
4 * (c) The GHC Team, 1998-2006
5 *
6 * Functions for parsing the argument list.
7 *
8 * ---------------------------------------------------------------------------*/
9
10 #include "PosixSource.h"
11 #include "Rts.h"
12
13 #include "RtsUtils.h"
14 #include "Profiling.h"
15 #include "RtsFlags.h"
16 #include "sm/OSMem.h"
17 #include "hooks/Hooks.h"
18
19 #ifdef HAVE_CTYPE_H
20 #include <ctype.h>
21 #endif
22
23 #include <string.h>
24
25 #ifdef HAVE_UNISTD_H
26 #include <unistd.h>
27 #endif
28
29 #ifdef HAVE_SYS_TYPES_H
30 #include <sys/types.h>
31 #endif
32
33 // Flag Structure
34 RTS_FLAGS RtsFlags;
35
36 /*
37 * Split argument lists
38 */
39 int prog_argc = 0; /* an "int" so as to match normal "argc" */
40 char **prog_argv = NULL;
41 int full_prog_argc = 0; /* an "int" so as to match normal "argc" */
42 char **full_prog_argv = NULL;
43 char *prog_name = NULL; /* 'basename' of prog_argv[0] */
44 int rts_argc = 0; /* ditto */
45 char **rts_argv = NULL;
46 int rts_argv_size = 0;
47 #if defined(mingw32_HOST_OS)
48 // On Windows, we want to use GetCommandLineW rather than argc/argv,
49 // but we need to mutate the command line arguments for withProgName and
50 // friends. The System.Environment module achieves that using this bit of
51 // shared state:
52 int win32_prog_argc = 0;
53 wchar_t **win32_prog_argv = NULL;
54 #endif
55
56 // The global rtsConfig, set from the RtsConfig supplied by the call
57 // to hs_init_ghc().
58 RtsConfig rtsConfig;
59
60 const RtsConfig defaultRtsConfig = {
61 .rts_opts_enabled = RtsOptsSafeOnly,
62 .rts_opts_suggestions = rtsTrue,
63 .rts_opts = NULL,
64 .rts_hs_main = rtsFalse,
65 .keep_cafs = rtsFalse,
66 .defaultsHook = FlagDefaultsHook,
67 .onExitHook = OnExitHook,
68 .stackOverflowHook = StackOverflowHook,
69 .outOfHeapHook = OutOfHeapHook,
70 .mallocFailHook = MallocFailHook,
71 .gcDoneHook = NULL
72 };
73
74 /*
75 * constants, used later
76 */
77 #define RTS 1
78 #define PGM 0
79
80 /* -----------------------------------------------------------------------------
81 Static function decls
82 -------------------------------------------------------------------------- */
83
84 static void procRtsOpts (int rts_argc0, RtsOptsEnabledEnum enabled);
85
86 static void normaliseRtsOpts (void);
87
88 static void initStatsFile (FILE *f);
89
90 static int openStatsFile (
91 char *filename, const char *FILENAME_FMT, FILE **file_ret);
92
93 static StgWord64 decodeSize (
94 const char *flag, nat offset, StgWord64 min, StgWord64 max);
95
96 static void bad_option (const char *s);
97
98 #ifdef DEBUG
99 static void read_debug_flags(const char *arg);
100 #endif
101
102 #ifdef PROFILING
103 static rtsBool read_heap_profiling_flag(const char *arg);
104 #endif
105
106 #ifdef TRACING
107 static void read_trace_flags(const char *arg);
108 #endif
109
110 static void errorUsage (void) GNU_ATTRIBUTE(__noreturn__);
111
112 static char * copyArg (char *arg);
113 static char ** copyArgv (int argc, char *argv[]);
114 static void freeArgv (int argc, char *argv[]);
115 static void setProgName (char *argv[]);
116
117 static void errorRtsOptsDisabled (const char *s);
118
119 /* -----------------------------------------------------------------------------
120 * Command-line option parsing routines.
121 * ---------------------------------------------------------------------------*/
122
123 void initRtsFlagsDefaults(void)
124 {
125 StgWord64 maxStkSize = 8 * getPhysicalMemorySize() / 10;
126 // if getPhysicalMemorySize fails just move along with an 8MB limit
127 if (maxStkSize == 0)
128 maxStkSize = 8 * 1024 * 1024;
129
130 RtsFlags.GcFlags.statsFile = NULL;
131 RtsFlags.GcFlags.giveStats = NO_GC_STATS;
132
133 RtsFlags.GcFlags.maxStkSize = maxStkSize / sizeof(W_);
134 RtsFlags.GcFlags.initialStkSize = 1024 / sizeof(W_);
135 RtsFlags.GcFlags.stkChunkSize = (32 * 1024) / sizeof(W_);
136 RtsFlags.GcFlags.stkChunkBufferSize = (1 * 1024) / sizeof(W_);
137
138 RtsFlags.GcFlags.minAllocAreaSize = (512 * 1024) / BLOCK_SIZE;
139 RtsFlags.GcFlags.largeAllocLim = 0; /* defaults to minAllocAreasize */
140 RtsFlags.GcFlags.nurseryChunkSize = 0;
141 RtsFlags.GcFlags.minOldGenSize = (1024 * 1024) / BLOCK_SIZE;
142 RtsFlags.GcFlags.maxHeapSize = 0; /* off by default */
143 RtsFlags.GcFlags.heapSizeSuggestion = 0; /* none */
144 RtsFlags.GcFlags.heapSizeSuggestionAuto = rtsFalse;
145 RtsFlags.GcFlags.pcFreeHeap = 3; /* 3% */
146 RtsFlags.GcFlags.oldGenFactor = 2;
147 RtsFlags.GcFlags.generations = 2;
148 RtsFlags.GcFlags.squeezeUpdFrames = rtsTrue;
149 RtsFlags.GcFlags.compact = rtsFalse;
150 RtsFlags.GcFlags.compactThreshold = 30.0;
151 RtsFlags.GcFlags.sweep = rtsFalse;
152 RtsFlags.GcFlags.idleGCDelayTime = USToTime(300000); // 300ms
153 #ifdef THREADED_RTS
154 RtsFlags.GcFlags.doIdleGC = rtsTrue;
155 #else
156 RtsFlags.GcFlags.doIdleGC = rtsFalse;
157 #endif
158 RtsFlags.GcFlags.heapBase = 0; /* means don't care */
159 RtsFlags.GcFlags.allocLimitGrace = (100*1024) / BLOCK_SIZE;
160
161 #ifdef DEBUG
162 RtsFlags.DebugFlags.scheduler = rtsFalse;
163 RtsFlags.DebugFlags.interpreter = rtsFalse;
164 RtsFlags.DebugFlags.weak = rtsFalse;
165 RtsFlags.DebugFlags.gccafs = rtsFalse;
166 RtsFlags.DebugFlags.gc = rtsFalse;
167 RtsFlags.DebugFlags.block_alloc = rtsFalse;
168 RtsFlags.DebugFlags.sanity = rtsFalse;
169 RtsFlags.DebugFlags.stable = rtsFalse;
170 RtsFlags.DebugFlags.stm = rtsFalse;
171 RtsFlags.DebugFlags.prof = rtsFalse;
172 RtsFlags.DebugFlags.apply = rtsFalse;
173 RtsFlags.DebugFlags.linker = rtsFalse;
174 RtsFlags.DebugFlags.squeeze = rtsFalse;
175 RtsFlags.DebugFlags.hpc = rtsFalse;
176 RtsFlags.DebugFlags.sparks = rtsFalse;
177 #endif
178
179 #if defined(PROFILING)
180 RtsFlags.CcFlags.doCostCentres = 0;
181 #endif /* PROFILING */
182
183 RtsFlags.ProfFlags.doHeapProfile = rtsFalse;
184 RtsFlags.ProfFlags. heapProfileInterval = USToTime(100000); // 100ms
185
186 #ifdef PROFILING
187 RtsFlags.ProfFlags.includeTSOs = rtsFalse;
188 RtsFlags.ProfFlags.showCCSOnException = rtsFalse;
189 RtsFlags.ProfFlags.maxRetainerSetSize = 8;
190 RtsFlags.ProfFlags.ccsLength = 25;
191 RtsFlags.ProfFlags.modSelector = NULL;
192 RtsFlags.ProfFlags.descrSelector = NULL;
193 RtsFlags.ProfFlags.typeSelector = NULL;
194 RtsFlags.ProfFlags.ccSelector = NULL;
195 RtsFlags.ProfFlags.ccsSelector = NULL;
196 RtsFlags.ProfFlags.retainerSelector = NULL;
197 RtsFlags.ProfFlags.bioSelector = NULL;
198 #endif
199
200 #ifdef TRACING
201 RtsFlags.TraceFlags.tracing = TRACE_NONE;
202 RtsFlags.TraceFlags.timestamp = rtsFalse;
203 RtsFlags.TraceFlags.scheduler = rtsFalse;
204 RtsFlags.TraceFlags.gc = rtsFalse;
205 RtsFlags.TraceFlags.sparks_sampled= rtsFalse;
206 RtsFlags.TraceFlags.sparks_full = rtsFalse;
207 RtsFlags.TraceFlags.user = rtsFalse;
208 #endif
209
210 #ifdef PROFILING
211 // When profiling we want a lot more ticks
212 RtsFlags.MiscFlags.tickInterval = USToTime(1000); // 1ms
213 #else
214 RtsFlags.MiscFlags.tickInterval = DEFAULT_TICK_INTERVAL;
215 #endif
216 RtsFlags.ConcFlags.ctxtSwitchTime = USToTime(20000); // 20ms
217
218 RtsFlags.MiscFlags.install_signal_handlers = rtsTrue;
219 RtsFlags.MiscFlags.machineReadable = rtsFalse;
220 RtsFlags.MiscFlags.linkerMemBase = 0;
221
222 #ifdef THREADED_RTS
223 RtsFlags.ParFlags.nNodes = 1;
224 RtsFlags.ParFlags.migrate = rtsTrue;
225 RtsFlags.ParFlags.parGcEnabled = 1;
226 RtsFlags.ParFlags.parGcGen = 0;
227 RtsFlags.ParFlags.parGcLoadBalancingEnabled = rtsTrue;
228 RtsFlags.ParFlags.parGcLoadBalancingGen = 1;
229 RtsFlags.ParFlags.parGcNoSyncWithIdle = 0;
230 RtsFlags.ParFlags.parGcThreads = 0; /* defaults to -N */
231 RtsFlags.ParFlags.setAffinity = 0;
232 #endif
233
234 #if defined(THREADED_RTS)
235 RtsFlags.ParFlags.maxLocalSparks = 4096;
236 #endif /* THREADED_RTS */
237
238 #ifdef TICKY_TICKY
239 RtsFlags.TickyFlags.showTickyStats = rtsFalse;
240 RtsFlags.TickyFlags.tickyFile = NULL;
241 #endif
242 }
243
244 static const char *
245 usage_text[] = {
246 "",
247 "Usage: <prog> <args> [+RTS <rtsopts> | -RTS <args>] ... --RTS <args>",
248 "",
249 " +RTS Indicates run time system options follow",
250 " -RTS Indicates program arguments follow",
251 " --RTS Indicates that ALL subsequent arguments will be given to the",
252 " program (including any of these RTS flags)",
253 "",
254 "The following run time system options are available:",
255 "",
256 " -? Prints this message and exits; the program is not executed",
257 " --info Print information about the RTS used by this program",
258 "",
259 " -K<size> Sets the maximum stack size (default: 80% of the heap)",
260 " Egs: -K32k -K512k -K8M",
261 " -ki<size> Sets the initial thread stack size (default 1k) Egs: -ki4k -ki2m",
262 " -kc<size> Sets the stack chunk size (default 32k)",
263 " -kb<size> Sets the stack chunk buffer size (default 1k)",
264 "",
265 " -A<size> Sets the minimum allocation area size (default 512k) Egs: -A1m -A10k",
266 " -AL<size> Sets the amount of large-object memory that can be allocated",
267 " before a GC is triggered (default: the value of -A)",
268 " -n<size> Allocation area chunk size (0 = disabled, default: 0)",
269 " -O<size> Sets the minimum size of the old generation (default 1M)",
270 " -M<size> Sets the maximum heap size (default unlimited) Egs: -M256k -M1G",
271 " -H<size> Sets the minimum heap size (default 0M) Egs: -H24m -H1G",
272 " -m<n> Minimum % of heap which must be available (default 3%)",
273 " -G<n> Number of generations (default: 2)",
274 " -c<n> Use in-place compaction instead of copying in the oldest generation",
275 " when live data is at least <n>% of the maximum heap size set with",
276 " -M (default: 30%)",
277 " -c Use in-place compaction for all oldest generation collections",
278 " (the default is to use copying)",
279 " -w Use mark-region for the oldest generation (experimental)",
280 #if defined(THREADED_RTS)
281 " -I<sec> Perform full GC after <sec> idle time (default: 0.3, 0 == off)",
282 #endif
283 "",
284 " -T Collect GC statistics (useful for in-program statistics access)",
285 " -t[<file>] One-line GC statistics (if <file> omitted, uses stderr)",
286 " -s[<file>] Summary GC statistics (if <file> omitted, uses stderr)",
287 " -S[<file>] Detailed GC statistics (if <file> omitted, uses stderr)",
288 "",
289 "",
290 " -Z Don't squeeze out update frames on stack overflow",
291 " -B Sound the bell at the start of each garbage collection",
292 #if defined(PROFILING)
293 "",
294 " -p Time/allocation profile (output file <program>.prof)",
295 " -P More detailed Time/Allocation profile",
296 " -Pa Give information about *all* cost centres",
297 "",
298 " -h<break-down> Heap residency profile (hp2ps) (output file <program>.hp)",
299 " break-down: c = cost centre stack (default)",
300 " m = module",
301 " d = closure description",
302 " y = type description",
303 " r = retainer",
304 " b = biography (LAG,DRAG,VOID,USE)",
305 " A subset of closures may be selected thusly:",
306 " -hc<cc>,... specific cost centre(s) (top of stack only)",
307 " -hC<cc>,... specific cost centre(s) (anywhere in stack)",
308 " -hm<mod>... all cost centres from the specified modules(s)",
309 " -hd<des>,... closures with specified closure descriptions",
310 " -hy<typ>... closures with specified type descriptions",
311 " -hr<cc>... closures with specified retainers",
312 " -hb<bio>... closures with specified biographies (lag,drag,void,use)",
313 "",
314 " -R<size> Set the maximum retainer set size (default: 8)",
315 "",
316 " -L<chars> Maximum length of a cost-centre stack in a heap profile",
317 " (default: 25)",
318 "",
319 " -xt Include threads (TSOs) in a heap profile",
320 "",
321 " -xc Show current cost centre stack on raising an exception",
322 #endif /* PROFILING */
323
324 #ifdef TRACING
325 "",
326 " -l[flags] Log events in binary format to the file <program>.eventlog",
327 # ifdef DEBUG
328 " -v[flags] Log events to stderr",
329 # endif
330 " where [flags] can contain:",
331 " s scheduler events",
332 " g GC and heap events",
333 " p par spark events (sampled)",
334 " f par spark events (full detail)",
335 " u user events (emitted from Haskell code)",
336 " a all event classes above",
337 # ifdef DEBUG
338 " t add time stamps (only useful with -v)",
339 # endif
340 " -x disable an event class, for any flag above",
341 " the initial enabled event classes are 'sgpu'",
342 #endif
343
344 #if !defined(PROFILING)
345 "",
346 " -h Heap residency profile (output file <program>.hp)",
347 #endif
348 " -i<sec> Time between heap profile samples (seconds, default: 0.1)",
349 "",
350 #if defined(TICKY_TICKY)
351 " -r<file> Produce ticky-ticky statistics (with -rstderr for stderr)",
352 "",
353 #endif
354 " -C<secs> Context-switch interval in seconds.",
355 " 0 or no argument means switch as often as possible.",
356 " Default: 0.02 sec.",
357 " -V<secs> Master tick interval in seconds (0 == disable timer).",
358 " This sets the resolution for -C and the heap profile timer -i,",
359 " and is the frequence of time profile samples.",
360 #ifdef PROFILING
361 " Default: 0.001 sec.",
362 #else
363 " Default: 0.01 sec.",
364 #endif
365 "",
366 #if defined(DEBUG)
367 " -Ds DEBUG: scheduler",
368 " -Di DEBUG: interpreter",
369 " -Dw DEBUG: weak",
370 " -DG DEBUG: gccafs",
371 " -Dg DEBUG: gc",
372 " -Db DEBUG: block",
373 " -DS DEBUG: sanity",
374 " -Dt DEBUG: stable",
375 " -Dp DEBUG: prof",
376 " -Da DEBUG: apply",
377 " -Dl DEBUG: linker",
378 " -Dm DEBUG: stm",
379 " -Dz DEBUG: stack squeezing",
380 " -Dc DEBUG: program coverage",
381 " -Dr DEBUG: sparks",
382 "",
383 " NOTE: DEBUG events are sent to stderr by default; add -l to create a",
384 " binary event log file instead.",
385 "",
386 #endif /* DEBUG */
387 #if defined(THREADED_RTS) && !defined(NOSMP)
388 " -N[<n>] Use <n> processors (default: 1, -N alone determines",
389 " the number of processors to use automatically)",
390 " -maxN[<n>] Use up to <n> processors automatically",
391 " -qg[<n>] Use parallel GC only for generations >= <n>",
392 " (default: 0, -qg alone turns off parallel GC)",
393 " -qb[<n>] Use load-balancing in the parallel GC only for generations >= <n>",
394 " (default: 1, -qb alone turns off load-balancing)",
395 " -qn<n> Use <n> threads for parallel GC (defaults to value of -N)",
396 " -qa Use the OS to set thread affinity (experimental)",
397 " -qm Don't automatically migrate threads between CPUs",
398 " -qi<n> If a processor has been idle for the last <n> GCs, do not",
399 " wake it up for a non-load-balancing parallel GC.",
400 " (0 disables, default: 0)",
401 #endif
402 " --install-signal-handlers=<yes|no>",
403 " Install signal handlers (default: yes)",
404 #if defined(THREADED_RTS)
405 " -e<n> Maximum number of outstanding local sparks (default: 4096)",
406 #endif
407 #if defined(x86_64_HOST_ARCH)
408 " -xm Base address to mmap memory in the GHCi linker",
409 " (hex; must be <80000000)",
410 #endif
411 " -xq The allocation limit given to a thread after it receives",
412 " an AllocationLimitExceeded exception. (default: 100k)",
413 "",
414 "RTS options may also be specified using the GHCRTS environment variable.",
415 "",
416 "Other RTS options may be available for programs compiled a different way.",
417 "The GHC User's Guide has full details.",
418 "",
419 0
420 };
421
422 STATIC_INLINE rtsBool strequal(const char *a, const char * b)
423 {
424 return(strcmp(a, b) == 0);
425 }
426
427 // We can't predict up front how much space we'll need for rts_argv,
428 // because it involves parsing ghc_rts_opts and GHCRTS, so we
429 // expand it on demand.
430 static void appendRtsArg (char *arg)
431 {
432 if (rts_argc == rts_argv_size) {
433 rts_argv_size *= 2;
434 rts_argv = stgReallocBytes(rts_argv, rts_argv_size * sizeof (char *),
435 "RtsFlags.c:appendRtsArg");
436 }
437 rts_argv[rts_argc++] = arg;
438 }
439
440 static void splitRtsFlags(const char *s)
441 {
442 const char *c1, *c2;
443 char *t;
444
445 c1 = s;
446 do {
447 while (isspace(*c1)) { c1++; };
448 c2 = c1;
449 while (!isspace(*c2) && *c2 != '\0') { c2++; };
450
451 if (c1 == c2) { break; }
452
453 t = stgMallocBytes(c2-c1+1, "RtsFlags.c:splitRtsFlags()");
454 strncpy(t, c1, c2-c1);
455 t[c2-c1] = '\0';
456 appendRtsArg(t);
457
458 c1 = c2;
459 } while (*c1 != '\0');
460 }
461
462 static void errorRtsOptsDisabled(const char *s)
463 {
464 char *advice;
465 if (rtsConfig.rts_hs_main) {
466 advice = "Link with -rtsopts to enable them.";
467 } else {
468 advice = "Use hs_init_with_rtsopts() to enable them.";
469 }
470 errorBelch(s, advice);
471 }
472
473 /* -----------------------------------------------------------------------------
474 Parse the command line arguments, collecting options for the RTS.
475
476 On return:
477 - argv[] is *modified*, any RTS options have been stripped out
478 - *argc contains the new count of arguments in argv[]
479
480 - rts_argv[] (global) contains a copy of the collected RTS args
481 - rts_argc (global) contains the count of args in rts_argv
482
483 - prog_argv[] (global) contains a copy of the non-RTS args (== argv)
484 - prog_argc (global) contains the count of args in prog_argv
485
486 - prog_name (global) contains the basename of prog_argv[0]
487
488 - rtsConfig (global) contains the supplied RtsConfig
489
490 -------------------------------------------------------------------------- */
491
492 void setupRtsFlags (int *argc, char *argv[], RtsConfig rts_config)
493 {
494 nat mode;
495 nat total_arg;
496 nat arg, rts_argc0;
497
498 rtsConfig = rts_config;
499
500 setProgName (argv);
501 total_arg = *argc;
502 arg = 1;
503
504 if (*argc > 1) { *argc = 1; };
505 rts_argc = 0;
506
507 rts_argv_size = total_arg + 1;
508 rts_argv = stgMallocBytes(rts_argv_size * sizeof (char *), "setupRtsFlags");
509
510 rts_argc0 = rts_argc;
511
512 // process arguments from the -with-rtsopts compile-time flag first
513 // (arguments from the GHCRTS environment variable and the command
514 // line override these).
515 {
516 if (rtsConfig.rts_opts != NULL) {
517 splitRtsFlags(rtsConfig.rts_opts);
518 // opts from rts_opts are always enabled:
519 procRtsOpts(rts_argc0, RtsOptsAll);
520 rts_argc0 = rts_argc;
521 }
522 }
523
524 // process arguments from the GHCRTS environment variable next
525 // (arguments from the command line override these).
526 {
527 char *ghc_rts = getenv("GHCRTS");
528
529 if (ghc_rts != NULL) {
530 if (rtsConfig.rts_opts_enabled == RtsOptsNone) {
531 errorRtsOptsDisabled(
532 "Warning: Ignoring GHCRTS variable as RTS options are disabled.\n %s");
533 // We don't actually exit, just warn
534 } else {
535 splitRtsFlags(ghc_rts);
536 procRtsOpts(rts_argc0, rtsConfig.rts_opts_enabled);
537 rts_argc0 = rts_argc;
538 }
539 }
540 }
541
542 // Split arguments (argv) into PGM (argv) and RTS (rts_argv) parts
543 // argv[0] must be PGM argument -- leave in argv
544
545 for (mode = PGM; arg < total_arg; arg++) {
546 // The '--RTS' argument disables all future +RTS ... -RTS processing.
547 if (strequal("--RTS", argv[arg])) {
548 arg++;
549 break;
550 }
551 // The '--' argument is passed through to the program, but
552 // disables all further +RTS ... -RTS processing.
553 else if (strequal("--", argv[arg])) {
554 break;
555 }
556 else if (strequal("+RTS", argv[arg])) {
557 mode = RTS;
558 }
559 else if (strequal("-RTS", argv[arg])) {
560 mode = PGM;
561 }
562 else if (mode == RTS) {
563 appendRtsArg(copyArg(argv[arg]));
564 }
565 else {
566 argv[(*argc)++] = argv[arg];
567 }
568 }
569 // process remaining program arguments
570 for (; arg < total_arg; arg++) {
571 argv[(*argc)++] = argv[arg];
572 }
573 argv[*argc] = (char *) 0;
574
575 procRtsOpts(rts_argc0, rtsConfig.rts_opts_enabled);
576
577 appendRtsArg((char *)0);
578 rts_argc--; // appendRtsArg will have bumped it for the NULL (#7227)
579
580 normaliseRtsOpts();
581
582 setProgArgv(*argc, argv);
583
584 if (RtsFlags.GcFlags.statsFile != NULL) {
585 initStatsFile (RtsFlags.GcFlags.statsFile);
586 }
587 #ifdef TICKY_TICKY
588 if (RtsFlags.TickyFlags.tickyFile != NULL) {
589 initStatsFile (RtsFlags.TickyFlags.tickyFile);
590 }
591 #endif
592 }
593
594 /* -----------------------------------------------------------------------------
595 * procRtsOpts: Process rts_argv between rts_argc0 and rts_argc.
596 * -------------------------------------------------------------------------- */
597
598 #if defined(HAVE_UNISTD_H) && defined(HAVE_SYS_TYPES_H) && !defined(mingw32_HOST_OS)
599 static void checkSuid(RtsOptsEnabledEnum enabled)
600 {
601 if (enabled == RtsOptsSafeOnly) {
602 /* This doesn't cover linux/posix capabilities like CAP_DAC_OVERRIDE,
603 we'd have to link with -lcap for that. */
604 if ((getuid() != geteuid()) || (getgid() != getegid())) {
605 errorRtsOptsDisabled(
606 "RTS options are disabled for setuid binaries. %s");
607 stg_exit(EXIT_FAILURE);
608 }
609 }
610 }
611 #else
612 static void checkSuid (RtsOptsEnabledEnum enabled STG_UNUSED)
613 {
614 }
615 #endif
616
617 static void checkUnsafe(RtsOptsEnabledEnum enabled)
618 {
619 if (enabled == RtsOptsSafeOnly) {
620 errorRtsOptsDisabled("Most RTS options are disabled. %s");
621 stg_exit(EXIT_FAILURE);
622 }
623 }
624
625 static void procRtsOpts (int rts_argc0,
626 RtsOptsEnabledEnum rtsOptsEnabled)
627 {
628 rtsBool error = rtsFalse;
629 int arg;
630 int unchecked_arg_start;
631
632 if (!(rts_argc0 < rts_argc)) return;
633
634 if (rtsOptsEnabled == RtsOptsNone) {
635 errorRtsOptsDisabled("RTS options are disabled. %s");
636 stg_exit(EXIT_FAILURE);
637 }
638
639 checkSuid(rtsOptsEnabled);
640
641 // Process RTS (rts_argv) part: mainly to determine statsfile
642 for (arg = rts_argc0; arg < rts_argc; arg++) {
643
644 /* We handle RtsOptsSafeOnly mode by declaring each option as
645 either OPTION_SAFE or OPTION_UNSAFE. To make sure we cover
646 every branch we use an option_checked flag which is reset
647 at the start each iteration and checked at the end. */
648 rtsBool option_checked = rtsFalse;
649
650 // See Note [OPTION_SAFE vs OPTION_UNSAFE].
651 #define OPTION_SAFE option_checked = rtsTrue;
652 #define OPTION_UNSAFE checkUnsafe(rtsOptsEnabled); option_checked = rtsTrue;
653
654 if (rts_argv[arg][0] != '-') {
655 fflush(stdout);
656 errorBelch("unexpected RTS argument: %s", rts_argv[arg]);
657 error = rtsTrue;
658
659 } else {
660 /* 0 is dash, 1 is first letter */
661 /* see Trac #9839 */
662 unchecked_arg_start = 1;
663 switch(rts_argv[arg][1]) {
664
665 /* process: general args, then PROFILING-only ones, then
666 CONCURRENT-only, TICKY-only (same order as defined in
667 RtsFlags.lh); within those groups, mostly in
668 case-insensitive alphabetical order. Final group is
669 x*, which allows for more options.
670 */
671
672 #ifdef TICKY_TICKY
673 # define TICKY_BUILD_ONLY(x) x
674 #else
675 # define TICKY_BUILD_ONLY(x) \
676 errorBelch("the flag %s requires the program to be built with -ticky", \
677 rts_argv[arg]); \
678 error = rtsTrue;
679 #endif
680
681 #ifdef PROFILING
682 # define PROFILING_BUILD_ONLY(x) x
683 #else
684 # define PROFILING_BUILD_ONLY(x) \
685 errorBelch("the flag %s requires the program to be built with -prof", \
686 rts_argv[arg]); \
687 error = rtsTrue;
688 #endif
689
690 #ifdef TRACING
691 # define TRACING_BUILD_ONLY(x) x
692 #else
693 # define TRACING_BUILD_ONLY(x) \
694 errorBelch("the flag %s requires the program to be built with -eventlog or -debug", \
695 rts_argv[arg]); \
696 error = rtsTrue;
697 #endif
698
699 #ifdef THREADED_RTS
700 # define THREADED_BUILD_ONLY(x) x
701 #else
702 # define THREADED_BUILD_ONLY(x) \
703 errorBelch("the flag %s requires the program to be built with -threaded", \
704 rts_argv[arg]); \
705 error = rtsTrue;
706 #endif
707
708 #ifdef DEBUG
709 # define DEBUG_BUILD_ONLY(x) x
710 #else
711 # define DEBUG_BUILD_ONLY(x) \
712 errorBelch("the flag %s requires the program to be built with -debug", \
713 rts_argv[arg]); \
714 error = rtsTrue;
715 #endif
716
717 /* =========== GENERAL ========================== */
718 case '?':
719 OPTION_SAFE;
720 error = rtsTrue;
721 break;
722
723 /* This isn't going to allow us to keep related options
724 together as we add more --* flags. We really need a
725 proper options parser. */
726 case '-':
727 if (strequal("install-signal-handlers=yes",
728 &rts_argv[arg][2])) {
729 OPTION_UNSAFE;
730 RtsFlags.MiscFlags.install_signal_handlers = rtsTrue;
731 }
732 else if (strequal("install-signal-handlers=no",
733 &rts_argv[arg][2])) {
734 OPTION_UNSAFE;
735 RtsFlags.MiscFlags.install_signal_handlers = rtsFalse;
736 }
737 else if (strequal("machine-readable",
738 &rts_argv[arg][2])) {
739 OPTION_UNSAFE;
740 RtsFlags.MiscFlags.machineReadable = rtsTrue;
741 }
742 else if (strequal("info",
743 &rts_argv[arg][2])) {
744 OPTION_SAFE;
745 printRtsInfo();
746 stg_exit(0);
747 }
748 else {
749 OPTION_SAFE;
750 errorBelch("unknown RTS option: %s",rts_argv[arg]);
751 error = rtsTrue;
752 }
753 break;
754 case 'A':
755 OPTION_UNSAFE;
756 if (rts_argv[arg][2] == 'L') {
757 RtsFlags.GcFlags.largeAllocLim
758 = decodeSize(rts_argv[arg], 3, 2*BLOCK_SIZE,
759 HS_INT_MAX) / BLOCK_SIZE;
760 } else {
761 // minimum two blocks in the nursery, so that we have one
762 // to grab for allocate().
763 RtsFlags.GcFlags.minAllocAreaSize
764 = decodeSize(rts_argv[arg], 2, 2*BLOCK_SIZE,
765 HS_INT_MAX) / BLOCK_SIZE;
766 }
767 break;
768 case 'n':
769 OPTION_UNSAFE;
770 RtsFlags.GcFlags.nurseryChunkSize
771 = decodeSize(rts_argv[arg], 2, 2*BLOCK_SIZE, HS_INT_MAX)
772 / BLOCK_SIZE;
773 break;
774
775 case 'B':
776 OPTION_UNSAFE;
777 RtsFlags.GcFlags.ringBell = rtsTrue;
778 unchecked_arg_start++;
779 goto check_rest;
780
781 case 'c':
782 OPTION_UNSAFE;
783 if (rts_argv[arg][2] != '\0') {
784 RtsFlags.GcFlags.compactThreshold =
785 atof(rts_argv[arg]+2);
786 } else {
787 RtsFlags.GcFlags.compact = rtsTrue;
788 }
789 break;
790
791 case 'w':
792 OPTION_UNSAFE;
793 RtsFlags.GcFlags.sweep = rtsTrue;
794 unchecked_arg_start++;
795 goto check_rest;
796
797 case 'F':
798 OPTION_UNSAFE;
799 RtsFlags.GcFlags.oldGenFactor = atof(rts_argv[arg]+2);
800
801 if (RtsFlags.GcFlags.oldGenFactor < 0)
802 bad_option( rts_argv[arg] );
803 break;
804
805 case 'D':
806 OPTION_SAFE;
807 DEBUG_BUILD_ONLY(read_debug_flags(rts_argv[arg]);)
808 break;
809
810 case 'K':
811 OPTION_UNSAFE;
812 RtsFlags.GcFlags.maxStkSize =
813 decodeSize(rts_argv[arg], 2, sizeof(W_), HS_WORD_MAX)
814 / sizeof(W_);
815 break;
816
817 case 'k':
818 OPTION_UNSAFE;
819 switch(rts_argv[arg][2]) {
820 case 'c':
821 RtsFlags.GcFlags.stkChunkSize =
822 decodeSize(rts_argv[arg], 3, sizeof(W_), HS_WORD_MAX)
823 / sizeof(W_);
824 break;
825 case 'b':
826 RtsFlags.GcFlags.stkChunkBufferSize =
827 decodeSize(rts_argv[arg], 3, sizeof(W_), HS_WORD_MAX)
828 / sizeof(W_);
829 break;
830 case 'i':
831 RtsFlags.GcFlags.initialStkSize =
832 decodeSize(rts_argv[arg], 3, sizeof(W_), HS_WORD_MAX)
833 / sizeof(W_);
834 break;
835 default:
836 RtsFlags.GcFlags.initialStkSize =
837 decodeSize(rts_argv[arg], 2, sizeof(W_), HS_WORD_MAX)
838 / sizeof(W_);
839 break;
840 }
841 break;
842
843 case 'M':
844 OPTION_UNSAFE;
845 RtsFlags.GcFlags.maxHeapSize =
846 decodeSize(rts_argv[arg], 2, BLOCK_SIZE, HS_WORD_MAX)
847 / BLOCK_SIZE;
848 /* user give size in *bytes* but "maxHeapSize" is in
849 * *blocks* */
850 break;
851
852 case 'm':
853 /* Case for maxN feature request ticket #10728, it's a little
854 odd being so far from the N case. */
855 #if !defined(NOSMP)
856 if (strncmp("maxN", &rts_argv[arg][1], 4) == 0) {
857 OPTION_SAFE;
858 THREADED_BUILD_ONLY(
859 int nNodes;
860 int proc = (int)getNumberOfProcessors();
861
862 nNodes = strtol(rts_argv[arg]+5, (char **) NULL, 10);
863 if (nNodes > proc) { nNodes = proc; }
864
865 if (nNodes <= 0) {
866 errorBelch("bad value for -maxN");
867 error = rtsTrue;
868 }
869 #if defined(PROFILING)
870 RtsFlags.ParFlags.nNodes = 1;
871 #else
872 RtsFlags.ParFlags.nNodes = (nat)nNodes;
873 #endif
874 ) break;
875 } else {
876 #endif
877 OPTION_UNSAFE;
878 RtsFlags.GcFlags.pcFreeHeap = atof(rts_argv[arg]+2);
879
880 /* -m was allowing bad flags to go unreported */
881 if (RtsFlags.GcFlags.pcFreeHeap == 0.0 &&
882 rts_argv[arg][2] != '0')
883 bad_option( rts_argv[arg] );
884
885 if (RtsFlags.GcFlags.pcFreeHeap < 0 ||
886 RtsFlags.GcFlags.pcFreeHeap > 100)
887 bad_option( rts_argv[arg] );
888 break;
889 #if !defined(NOSMP)
890 }
891 #endif
892 case 'G':
893 OPTION_UNSAFE;
894 RtsFlags.GcFlags.generations =
895 decodeSize(rts_argv[arg], 2, 1, HS_INT_MAX);
896 break;
897
898 case 'H':
899 OPTION_UNSAFE;
900 if (rts_argv[arg][2] == '\0') {
901 RtsFlags.GcFlags.heapSizeSuggestionAuto = rtsTrue;
902 } else {
903 RtsFlags.GcFlags.heapSizeSuggestion =
904 (nat)(decodeSize(rts_argv[arg], 2, BLOCK_SIZE, HS_WORD_MAX) / BLOCK_SIZE);
905 }
906 break;
907
908 case 'O':
909 OPTION_UNSAFE;
910 RtsFlags.GcFlags.minOldGenSize =
911 (nat)(decodeSize(rts_argv[arg], 2, BLOCK_SIZE,
912 HS_WORD_MAX)
913 / BLOCK_SIZE);
914 break;
915
916 case 'I': /* idle GC delay */
917 OPTION_UNSAFE;
918 if (rts_argv[arg][2] == '\0') {
919 /* use default */
920 } else {
921 Time t = fsecondsToTime(atof(rts_argv[arg]+2));
922 if (t == 0) {
923 RtsFlags.GcFlags.doIdleGC = rtsFalse;
924 } else {
925 RtsFlags.GcFlags.doIdleGC = rtsTrue;
926 RtsFlags.GcFlags.idleGCDelayTime = t;
927 }
928 }
929 break;
930
931 case 'T':
932 OPTION_SAFE;
933 RtsFlags.GcFlags.giveStats = COLLECT_GC_STATS;
934 unchecked_arg_start++;
935 goto check_rest; /* Don't initialize statistics file. */
936
937 case 'S':
938 OPTION_SAFE; /* but see below */
939 RtsFlags.GcFlags.giveStats = VERBOSE_GC_STATS;
940 goto stats;
941
942 case 's':
943 OPTION_SAFE; /* but see below */
944 RtsFlags.GcFlags.giveStats = SUMMARY_GC_STATS;
945 goto stats;
946
947 case 't':
948 OPTION_SAFE; /* but see below */
949 RtsFlags.GcFlags.giveStats = ONELINE_GC_STATS;
950 goto stats;
951
952 stats:
953 {
954 int r;
955 if (rts_argv[arg][2] != '\0') {
956 OPTION_UNSAFE;
957 }
958 r = openStatsFile(rts_argv[arg]+2, NULL,
959 &RtsFlags.GcFlags.statsFile);
960 if (r == -1) { error = rtsTrue; }
961 }
962 break;
963
964 case 'Z':
965 OPTION_UNSAFE;
966 RtsFlags.GcFlags.squeezeUpdFrames = rtsFalse;
967 unchecked_arg_start++;
968 goto check_rest;
969
970 /* =========== PROFILING ========================== */
971
972 case 'P': /* detailed cost centre profiling (time/alloc) */
973 case 'p': /* cost centre profiling (time/alloc) */
974 OPTION_SAFE;
975 PROFILING_BUILD_ONLY(
976 switch (rts_argv[arg][2]) {
977 case 'a':
978 RtsFlags.CcFlags.doCostCentres = COST_CENTRES_ALL;
979 if (rts_argv[arg][3] != '\0') {
980 errorBelch("flag -Pa given an argument"
981 " when none was expected: %s"
982 ,rts_argv[arg]);
983 error = rtsTrue;
984 }
985 break;
986 case '\0':
987 if (rts_argv[arg][1] == 'P') {
988 RtsFlags.CcFlags.doCostCentres =
989 COST_CENTRES_VERBOSE;
990 } else {
991 RtsFlags.CcFlags.doCostCentres =
992 COST_CENTRES_SUMMARY;
993 }
994 break;
995 default:
996 unchecked_arg_start++;
997 goto check_rest;
998 }
999 ) break;
1000
1001 case 'R':
1002 OPTION_SAFE;
1003 PROFILING_BUILD_ONLY(
1004 RtsFlags.ProfFlags.maxRetainerSetSize =
1005 atof(rts_argv[arg]+2);
1006 ) break;
1007 case 'L':
1008 OPTION_SAFE;
1009 PROFILING_BUILD_ONLY(
1010 RtsFlags.ProfFlags.ccsLength = atof(rts_argv[arg]+2);
1011 if(RtsFlags.ProfFlags.ccsLength <= 0) {
1012 bad_option(rts_argv[arg]);
1013 }
1014 ) break;
1015 case 'h': /* serial heap profile */
1016 #if !defined(PROFILING)
1017 switch (rts_argv[arg][2]) {
1018 case '\0':
1019 case 'T':
1020 OPTION_UNSAFE;
1021 RtsFlags.ProfFlags.doHeapProfile = HEAP_BY_CLOSURE_TYPE;
1022 break;
1023 default:
1024 OPTION_SAFE;
1025 PROFILING_BUILD_ONLY();
1026 }
1027 #else
1028 OPTION_SAFE;
1029 PROFILING_BUILD_ONLY(
1030 error = read_heap_profiling_flag(rts_argv[arg]);
1031 );
1032 #endif /* PROFILING */
1033 break;
1034
1035 case 'i': /* heap sample interval */
1036 OPTION_UNSAFE;
1037 if (rts_argv[arg][2] == '\0') {
1038 /* use default */
1039 } else {
1040 RtsFlags.ProfFlags.heapProfileInterval =
1041 fsecondsToTime(atof(rts_argv[arg]+2));
1042 }
1043 break;
1044
1045 /* =========== CONCURRENT ========================= */
1046 case 'C': /* context switch interval */
1047 OPTION_UNSAFE;
1048 if (rts_argv[arg][2] == '\0')
1049 RtsFlags.ConcFlags.ctxtSwitchTime = 0;
1050 else {
1051 RtsFlags.ConcFlags.ctxtSwitchTime =
1052 fsecondsToTime(atof(rts_argv[arg]+2));
1053 }
1054 break;
1055
1056 case 'V': /* master tick interval */
1057 OPTION_UNSAFE;
1058 if (rts_argv[arg][2] == '\0') {
1059 // turns off ticks completely
1060 RtsFlags.MiscFlags.tickInterval = 0;
1061 } else {
1062 RtsFlags.MiscFlags.tickInterval =
1063 fsecondsToTime(atof(rts_argv[arg]+2));
1064 }
1065 break;
1066
1067 #if !defined(NOSMP)
1068 case 'N':
1069 OPTION_SAFE;
1070 THREADED_BUILD_ONLY(
1071 if (rts_argv[arg][2] == '\0') {
1072 #if defined(PROFILING)
1073 RtsFlags.ParFlags.nNodes = 1;
1074 #else
1075 RtsFlags.ParFlags.nNodes = getNumberOfProcessors();
1076 #endif
1077 } else {
1078 int nNodes;
1079 OPTION_SAFE; /* but see extra checks below... */
1080
1081 nNodes = strtol(rts_argv[arg]+2, (char **) NULL, 10);
1082
1083 if (nNodes <= 0) {
1084 errorBelch("bad value for -N");
1085 error = rtsTrue;
1086 }
1087 if (rtsOptsEnabled == RtsOptsSafeOnly &&
1088 nNodes > (int)getNumberOfProcessors()) {
1089 errorRtsOptsDisabled("Using large values for -N is not allowed by default. %s");
1090 stg_exit(EXIT_FAILURE);
1091 }
1092 RtsFlags.ParFlags.nNodes = (nat)nNodes;
1093 }
1094 ) break;
1095
1096 case 'g':
1097 OPTION_UNSAFE;
1098 THREADED_BUILD_ONLY(
1099 switch (rts_argv[arg][2]) {
1100 case '1':
1101 // backwards compat only
1102 RtsFlags.ParFlags.parGcEnabled = rtsFalse;
1103 break;
1104 default:
1105 errorBelch("unknown RTS option: %s",rts_argv[arg]);
1106 error = rtsTrue;
1107 break;
1108 }
1109 ) break;
1110
1111 case 'q':
1112 OPTION_UNSAFE;
1113 THREADED_BUILD_ONLY(
1114 switch (rts_argv[arg][2]) {
1115 case '\0':
1116 errorBelch("incomplete RTS option: %s",rts_argv[arg]);
1117 error = rtsTrue;
1118 break;
1119 case 'g':
1120 if (rts_argv[arg][3] == '\0') {
1121 RtsFlags.ParFlags.parGcEnabled = rtsFalse;
1122 } else {
1123 RtsFlags.ParFlags.parGcEnabled = rtsTrue;
1124 RtsFlags.ParFlags.parGcGen
1125 = strtol(rts_argv[arg]+3, (char **) NULL, 10);
1126 }
1127 break;
1128 case 'b':
1129 if (rts_argv[arg][3] == '\0') {
1130 RtsFlags.ParFlags.parGcLoadBalancingEnabled =
1131 rtsFalse;
1132 }
1133 else {
1134 RtsFlags.ParFlags.parGcLoadBalancingEnabled =
1135 rtsTrue;
1136 RtsFlags.ParFlags.parGcLoadBalancingGen
1137 = strtol(rts_argv[arg]+3, (char **) NULL, 10);
1138 }
1139 break;
1140 case 'i':
1141 RtsFlags.ParFlags.parGcNoSyncWithIdle
1142 = strtol(rts_argv[arg]+3, (char **) NULL, 10);
1143 break;
1144 case 'n': {
1145 int threads;
1146 threads = strtol(rts_argv[arg]+3, (char **) NULL, 10);
1147 if (threads <= 0) {
1148 errorBelch("-qn must be 1 or greater");
1149 error = rtsTrue;
1150 } else {
1151 RtsFlags.ParFlags.parGcThreads = threads;
1152 }
1153 break;
1154 }
1155 case 'a':
1156 RtsFlags.ParFlags.setAffinity = rtsTrue;
1157 break;
1158 case 'm':
1159 RtsFlags.ParFlags.migrate = rtsFalse;
1160 break;
1161 case 'w':
1162 // -qw was removed; accepted for backwards compat
1163 break;
1164 default:
1165 errorBelch("unknown RTS option: %s",rts_argv[arg]);
1166 error = rtsTrue;
1167 break;
1168 }
1169 ) break;
1170 #endif
1171 /* =========== PARALLEL =========================== */
1172 case 'e':
1173 OPTION_UNSAFE;
1174 THREADED_BUILD_ONLY(
1175 if (rts_argv[arg][2] != '\0') {
1176 RtsFlags.ParFlags.maxLocalSparks
1177 = strtol(rts_argv[arg]+2, (char **) NULL, 10);
1178 if (RtsFlags.ParFlags.maxLocalSparks <= 0) {
1179 errorBelch("bad value for -e");
1180 error = rtsTrue;
1181 }
1182 }
1183 ) break;
1184
1185 /* =========== TICKY ============================== */
1186
1187 case 'r': /* Basic profiling stats */
1188 OPTION_SAFE;
1189 TICKY_BUILD_ONLY(
1190
1191 RtsFlags.TickyFlags.showTickyStats = rtsTrue;
1192
1193 {
1194 int r;
1195 if (rts_argv[arg][2] != '\0') {
1196 OPTION_UNSAFE;
1197 }
1198 r = openStatsFile(rts_argv[arg]+2,
1199 TICKY_FILENAME_FMT,
1200 &RtsFlags.TickyFlags.tickyFile);
1201 if (r == -1) { error = rtsTrue; }
1202 }
1203 ) break;
1204
1205 /* =========== TRACING ---------=================== */
1206
1207 case 'l':
1208 OPTION_SAFE;
1209 TRACING_BUILD_ONLY(
1210 RtsFlags.TraceFlags.tracing = TRACE_EVENTLOG;
1211 read_trace_flags(&rts_argv[arg][2]);
1212 );
1213 break;
1214
1215 case 'v':
1216 OPTION_SAFE;
1217 DEBUG_BUILD_ONLY(
1218 RtsFlags.TraceFlags.tracing = TRACE_STDERR;
1219 read_trace_flags(&rts_argv[arg][2]);
1220 );
1221 break;
1222
1223 /* =========== EXTENDED OPTIONS =================== */
1224
1225 case 'x': /* Extend the argument space */
1226 unchecked_arg_start++;
1227 switch(rts_argv[arg][2]) {
1228 case '\0':
1229 OPTION_SAFE;
1230 errorBelch("incomplete RTS option: %s",rts_argv[arg]);
1231 error = rtsTrue;
1232 break;
1233
1234 case 'b': /* heapBase in hex; undocumented */
1235 OPTION_UNSAFE;
1236 if (rts_argv[arg][3] != '\0') {
1237 RtsFlags.GcFlags.heapBase
1238 = strtol(rts_argv[arg]+3, (char **) NULL, 16);
1239 } else {
1240 errorBelch("-xb: requires argument");
1241 error = rtsTrue;
1242 }
1243 break;
1244
1245 #if defined(x86_64_HOST_ARCH)
1246 case 'm': /* linkerMemBase */
1247 OPTION_UNSAFE;
1248 if (rts_argv[arg][3] != '\0') {
1249 RtsFlags.MiscFlags.linkerMemBase
1250 = strtol(rts_argv[arg]+3, (char **) NULL, 16);
1251 if (RtsFlags.MiscFlags.linkerMemBase > 0x80000000) {
1252 errorBelch("-xm: value must be <80000000");
1253 error = rtsTrue;
1254 }
1255 } else {
1256 RtsFlags.MiscFlags.linkerMemBase = 0;
1257 }
1258 break;
1259 #endif
1260
1261 case 'c': /* Debugging tool: show current cost centre on
1262 an exception */
1263 OPTION_SAFE;
1264 PROFILING_BUILD_ONLY(
1265 RtsFlags.ProfFlags.showCCSOnException = rtsTrue;
1266 );
1267 unchecked_arg_start++;
1268 goto check_rest;
1269
1270 case 't': /* Include memory used by TSOs in a heap profile */
1271 OPTION_SAFE;
1272 PROFILING_BUILD_ONLY(
1273 RtsFlags.ProfFlags.includeTSOs = rtsTrue;
1274 );
1275 unchecked_arg_start++;
1276 goto check_rest;
1277
1278 /*
1279 * The option prefix '-xx' is reserved for future
1280 * extension. KSW 1999-11.
1281 */
1282
1283 case 'q':
1284 OPTION_UNSAFE;
1285 RtsFlags.GcFlags.allocLimitGrace
1286 = decodeSize(rts_argv[arg], 3, BLOCK_SIZE, HS_INT_MAX)
1287 / BLOCK_SIZE;
1288 break;
1289
1290 default:
1291 OPTION_SAFE;
1292 errorBelch("unknown RTS option: %s",rts_argv[arg]);
1293 error = rtsTrue;
1294 break;
1295 }
1296 break; /* defensive programming */
1297
1298 /* check the rest to be sure there is nothing afterwards.*/
1299 /* see Trac #9839 */
1300 check_rest:
1301 {
1302 /* start checking from the first unchecked position,
1303 * not from index 2*/
1304 /* see Trac #9839 */
1305 if (rts_argv[arg][unchecked_arg_start] != '\0') {
1306 errorBelch("flag -%c given an argument"
1307 " when none was expected: %s",
1308 rts_argv[arg][1],rts_argv[arg]);
1309 error = rtsTrue;
1310 }
1311 break;
1312 }
1313
1314 /* =========== OH DEAR ============================ */
1315 default:
1316 OPTION_SAFE;
1317 errorBelch("unknown RTS option: %s",rts_argv[arg]);
1318 error = rtsTrue;
1319 break;
1320 }
1321
1322 if (!option_checked) {
1323 /* Naughty! Someone didn't use OPTION_UNSAFE / OPTION_SAFE for
1324 an option above */
1325 errorBelch("Internal error in the RTS options parser");
1326 stg_exit(EXIT_FAILURE);
1327 }
1328 }
1329 }
1330
1331 if (error) errorUsage();
1332 }
1333
1334 /* -----------------------------------------------------------------------------
1335 * normaliseRtsOpts: Set some derived values, and make sure things are
1336 * within sensible ranges.
1337 * -------------------------------------------------------------------------- */
1338
1339 static void normaliseRtsOpts (void)
1340 {
1341 if (RtsFlags.MiscFlags.tickInterval < 0) {
1342 RtsFlags.MiscFlags.tickInterval = DEFAULT_TICK_INTERVAL;
1343 }
1344
1345 // If the master timer is disabled, turn off the other timers.
1346 if (RtsFlags.MiscFlags.tickInterval == 0) {
1347 RtsFlags.ConcFlags.ctxtSwitchTime = 0;
1348 RtsFlags.GcFlags.idleGCDelayTime = 0;
1349 RtsFlags.ProfFlags.heapProfileInterval = 0;
1350 }
1351
1352 // Determine what tick interval we should use for the RTS timer
1353 // by taking the shortest of the various intervals that we need to
1354 // monitor.
1355 if (RtsFlags.ConcFlags.ctxtSwitchTime > 0) {
1356 RtsFlags.MiscFlags.tickInterval =
1357 stg_min(RtsFlags.ConcFlags.ctxtSwitchTime,
1358 RtsFlags.MiscFlags.tickInterval);
1359 }
1360
1361 if (RtsFlags.GcFlags.idleGCDelayTime > 0) {
1362 RtsFlags.MiscFlags.tickInterval =
1363 stg_min(RtsFlags.GcFlags.idleGCDelayTime,
1364 RtsFlags.MiscFlags.tickInterval);
1365 }
1366
1367 if (RtsFlags.ProfFlags.heapProfileInterval > 0) {
1368 RtsFlags.MiscFlags.tickInterval =
1369 stg_min(RtsFlags.ProfFlags.heapProfileInterval,
1370 RtsFlags.MiscFlags.tickInterval);
1371 }
1372
1373 if (RtsFlags.ConcFlags.ctxtSwitchTime > 0) {
1374 RtsFlags.ConcFlags.ctxtSwitchTicks =
1375 RtsFlags.ConcFlags.ctxtSwitchTime /
1376 RtsFlags.MiscFlags.tickInterval;
1377 } else {
1378 RtsFlags.ConcFlags.ctxtSwitchTicks = 0;
1379 }
1380
1381 if (RtsFlags.ProfFlags.heapProfileInterval > 0) {
1382 RtsFlags.ProfFlags.heapProfileIntervalTicks =
1383 RtsFlags.ProfFlags.heapProfileInterval /
1384 RtsFlags.MiscFlags.tickInterval;
1385 } else {
1386 RtsFlags.ProfFlags.heapProfileIntervalTicks = 0;
1387 }
1388
1389 if (RtsFlags.GcFlags.stkChunkBufferSize >
1390 RtsFlags.GcFlags.stkChunkSize / 2) {
1391 errorBelch("stack chunk buffer size (-kb) must be less than 50%%\n"
1392 "of the stack chunk size (-kc)");
1393 errorUsage();
1394 }
1395
1396 #ifdef THREADED_RTS
1397 if (RtsFlags.ParFlags.parGcThreads > RtsFlags.ParFlags.nNodes) {
1398 errorBelch("GC threads (-qn) must be between 1 and the value of -N");
1399 errorUsage();
1400 }
1401 #endif
1402 }
1403
1404 static void errorUsage (void)
1405 {
1406 const char **p;
1407
1408 fflush(stdout);
1409 for (p = usage_text; *p; p++)
1410 errorBelch("%s", *p);
1411 stg_exit(EXIT_FAILURE);
1412 }
1413
1414 static void
1415 stats_fprintf(FILE *f, char *s, ...)
1416 {
1417 va_list ap;
1418 va_start(ap,s);
1419 if (f == NULL) {
1420 vdebugBelch(s, ap);
1421 } else {
1422 vfprintf(f, s, ap);
1423 }
1424 va_end(ap);
1425 }
1426
1427 /* -----------------------------------------------------------------------------
1428 * openStatsFile: open a file in which to put some runtime stats
1429 * -------------------------------------------------------------------------- */
1430
1431 static int // return -1 on error
1432 openStatsFile (char *filename, // filename, or NULL
1433 const char *filename_fmt, // if filename == NULL, use
1434 // this fmt with sprintf to
1435 // generate the filename. %s
1436 // expands to the program name.
1437 FILE **file_ret) // return the FILE*
1438 {
1439 FILE *f = NULL;
1440
1441 if (strequal(filename, "stderr")
1442 || (filename_fmt == NULL && *filename == '\0')) {
1443 f = NULL; /* NULL means use debugBelch */
1444 } else {
1445 if (*filename != '\0') { /* stats file specified */
1446 f = fopen(filename,"w");
1447 } else {
1448 /* default <program>.<ext> */
1449 char stats_filename[STATS_FILENAME_MAXLEN];
1450 sprintf(stats_filename, filename_fmt, prog_name);
1451 f = fopen(stats_filename,"w");
1452 }
1453 if (f == NULL) {
1454 errorBelch("Can't open stats file %s\n", filename);
1455 return -1;
1456 }
1457 }
1458 *file_ret = f;
1459
1460 return 0;
1461 }
1462
1463 /* -----------------------------------------------------------------------------
1464 * initStatsFile: write a line to the file containing the program name
1465 * and the arguments it was invoked with.
1466 -------------------------------------------------------------------------- */
1467
1468 static void initStatsFile (FILE *f)
1469 {
1470 /* Write prog_argv and rts_argv into start of stats file */
1471 int count;
1472 for (count = 0; count < prog_argc; count++) {
1473 stats_fprintf(f, "%s ", prog_argv[count]);
1474 }
1475 stats_fprintf(f, "+RTS ");
1476 for (count = 0; count < rts_argc; count++)
1477 stats_fprintf(f, "%s ", rts_argv[count]);
1478 stats_fprintf(f, "\n");
1479 }
1480
1481 /* -----------------------------------------------------------------------------
1482 * decodeSize: parse a string containing a size, like 300K or 1.2M
1483 -------------------------------------------------------------------------- */
1484
1485 static StgWord64
1486 decodeSize(const char *flag, nat offset, StgWord64 min, StgWord64 max)
1487 {
1488 char c;
1489 const char *s;
1490 StgDouble m;
1491 StgWord64 val;
1492
1493 s = flag + offset;
1494
1495 if (!*s)
1496 {
1497 m = 0;
1498 }
1499 else
1500 {
1501 m = atof(s);
1502 c = s[strlen(s)-1];
1503
1504 if (c == 'g' || c == 'G')
1505 m *= 1024*1024*1024;
1506 else if (c == 'm' || c == 'M')
1507 m *= 1024*1024;
1508 else if (c == 'k' || c == 'K')
1509 m *= 1024;
1510 else if (c == 'w' || c == 'W')
1511 m *= sizeof(W_);
1512 }
1513
1514 val = (StgWord64)m;
1515
1516 if (m < 0 || val < min || val > max) {
1517 // printf doesn't like 64-bit format specs on Windows
1518 // apparently, so fall back to unsigned long.
1519 errorBelch("error in RTS option %s: size outside allowed range (%" FMT_Word " - %" FMT_Word ")", flag, (W_)min, (W_)max);
1520 stg_exit(EXIT_FAILURE);
1521 }
1522
1523 return val;
1524 }
1525
1526 #ifdef DEBUG
1527 static void read_debug_flags(const char* arg)
1528 {
1529 // Already parsed "-D"
1530 const char *c;
1531 for (c = arg + 2; *c != '\0'; c++) {
1532 switch (*c) {
1533 case 's':
1534 RtsFlags.DebugFlags.scheduler = rtsTrue;
1535 break;
1536 case 'i':
1537 RtsFlags.DebugFlags.interpreter = rtsTrue;
1538 break;
1539 case 'w':
1540 RtsFlags.DebugFlags.weak = rtsTrue;
1541 break;
1542 case 'G':
1543 RtsFlags.DebugFlags.gccafs = rtsTrue;
1544 break;
1545 case 'g':
1546 RtsFlags.DebugFlags.gc = rtsTrue;
1547 break;
1548 case 'b':
1549 RtsFlags.DebugFlags.block_alloc = rtsTrue;
1550 break;
1551 case 'S':
1552 RtsFlags.DebugFlags.sanity = rtsTrue;
1553 break;
1554 case 't':
1555 RtsFlags.DebugFlags.stable = rtsTrue;
1556 break;
1557 case 'p':
1558 RtsFlags.DebugFlags.prof = rtsTrue;
1559 break;
1560 case 'l':
1561 RtsFlags.DebugFlags.linker = rtsTrue;
1562 break;
1563 case 'a':
1564 RtsFlags.DebugFlags.apply = rtsTrue;
1565 break;
1566 case 'm':
1567 RtsFlags.DebugFlags.stm = rtsTrue;
1568 break;
1569 case 'z':
1570 RtsFlags.DebugFlags.squeeze = rtsTrue;
1571 break;
1572 case 'c':
1573 RtsFlags.DebugFlags.hpc = rtsTrue;
1574 break;
1575 case 'r':
1576 RtsFlags.DebugFlags.sparks = rtsTrue;
1577 break;
1578 default:
1579 bad_option( arg );
1580 }
1581 }
1582 // -Dx also turns on -v. Use -l to direct trace
1583 // events to the .eventlog file instead.
1584 RtsFlags.TraceFlags.tracing = TRACE_STDERR;
1585 }
1586 #endif
1587
1588 #ifdef PROFILING
1589 // Parse a "-h" flag, returning whether the parse resulted in an error.
1590 static rtsBool read_heap_profiling_flag(const char *arg_in)
1591 {
1592 // Already parsed "-h"
1593
1594 // For historical reasons the parser here mutates the arguments.
1595 // However, for sanity we want to guarantee const-correctness and parsing
1596 // really ought to be an immutable operation. To avoid rewriting the parser
1597 // we just operate on a temporary copy of the argument.
1598 char *arg = strdup(arg_in);
1599 rtsBool error = rtsFalse;
1600 switch (arg[2]) {
1601 case '\0':
1602 case 'C':
1603 case 'c':
1604 case 'M':
1605 case 'm':
1606 case 'D':
1607 case 'd':
1608 case 'Y':
1609 case 'y':
1610 case 'R':
1611 case 'r':
1612 case 'B':
1613 case 'b':
1614 if (arg[2] != '\0' && arg[3] != '\0') {
1615 {
1616 char *left = strchr(arg, '{');
1617 char *right = strrchr(arg, '}');
1618
1619 // curly braces are optional, for
1620 // backwards compat.
1621 if (left)
1622 left = left+1;
1623 else
1624 left = arg + 3;
1625
1626 if (!right)
1627 right = arg + strlen(arg);
1628
1629 char *selector = stgStrndup(left, right - left + 1);
1630
1631 switch (arg[2]) {
1632 case 'c': // cost centre label select
1633 RtsFlags.ProfFlags.ccSelector = selector;
1634 break;
1635 case 'C':
1636 RtsFlags.ProfFlags.ccsSelector = selector;
1637 break;
1638 case 'M':
1639 case 'm': // cost centre module select
1640 RtsFlags.ProfFlags.modSelector = selector;
1641 break;
1642 case 'D':
1643 case 'd': // closure descr select
1644 RtsFlags.ProfFlags.descrSelector = selector;
1645 break;
1646 case 'Y':
1647 case 'y': // closure type select
1648 RtsFlags.ProfFlags.typeSelector = selector;
1649 break;
1650 case 'R':
1651 case 'r': // retainer select
1652 RtsFlags.ProfFlags.retainerSelector = selector;
1653 break;
1654 case 'B':
1655 case 'b': // biography select
1656 RtsFlags.ProfFlags.bioSelector = selector;
1657 break;
1658 default:
1659 free(selector);
1660 }
1661 }
1662 break;
1663 }
1664
1665 if (RtsFlags.ProfFlags.doHeapProfile != 0) {
1666 errorBelch("multiple heap profile options");
1667 error = rtsTrue;
1668 break;
1669 }
1670
1671 switch (arg[2]) {
1672 case '\0':
1673 case 'C':
1674 case 'c':
1675 RtsFlags.ProfFlags.doHeapProfile = HEAP_BY_CCS;
1676 break;
1677 case 'M':
1678 case 'm':
1679 RtsFlags.ProfFlags.doHeapProfile = HEAP_BY_MOD;
1680 break;
1681 case 'D':
1682 case 'd':
1683 RtsFlags.ProfFlags.doHeapProfile = HEAP_BY_DESCR;
1684 break;
1685 case 'Y':
1686 case 'y':
1687 RtsFlags.ProfFlags.doHeapProfile = HEAP_BY_TYPE;
1688 break;
1689 case 'R':
1690 case 'r':
1691 RtsFlags.ProfFlags.doHeapProfile = HEAP_BY_RETAINER;
1692 break;
1693 case 'B':
1694 case 'b':
1695 RtsFlags.ProfFlags.doHeapProfile = HEAP_BY_LDV;
1696 break;
1697 }
1698 break;
1699
1700 default:
1701 errorBelch("invalid heap profile option: %s", arg);
1702 error = rtsTrue;
1703 }
1704
1705 free(arg);
1706 return error;
1707 }
1708 #endif
1709
1710 #if defined(TRACING)
1711 static void read_trace_flags(const char *arg)
1712 {
1713 const char *c;
1714 rtsBool enabled = rtsTrue;
1715 /* Syntax for tracing flags currently looks like:
1716 *
1717 * -l To turn on eventlog tracing with default trace classes
1718 * -lx Turn on class 'x' (for some class listed below)
1719 * -l-x Turn off class 'x'
1720 * -la Turn on all classes
1721 * -l-a Turn off all classes
1722 *
1723 * This lets users say things like:
1724 * -la-p "all but sparks"
1725 * -l-ap "only sparks"
1726 */
1727
1728 /* Start by turning on the default tracing flags.
1729 *
1730 * Currently this is all the trace classes, except full-detail sparks.
1731 * Similarly, in future we might default to slightly less verbose
1732 * scheduler or GC tracing.
1733 */
1734 RtsFlags.TraceFlags.scheduler = rtsTrue;
1735 RtsFlags.TraceFlags.gc = rtsTrue;
1736 RtsFlags.TraceFlags.sparks_sampled = rtsTrue;
1737 RtsFlags.TraceFlags.user = rtsTrue;
1738
1739 for (c = arg; *c != '\0'; c++) {
1740 switch(*c) {
1741 case '\0':
1742 break;
1743 case '-':
1744 enabled = rtsFalse;
1745 break;
1746 case 'a':
1747 RtsFlags.TraceFlags.scheduler = enabled;
1748 RtsFlags.TraceFlags.gc = enabled;
1749 RtsFlags.TraceFlags.sparks_sampled = enabled;
1750 RtsFlags.TraceFlags.sparks_full = enabled;
1751 RtsFlags.TraceFlags.user = enabled;
1752 enabled = rtsTrue;
1753 break;
1754
1755 case 's':
1756 RtsFlags.TraceFlags.scheduler = enabled;
1757 enabled = rtsTrue;
1758 break;
1759 case 'p':
1760 RtsFlags.TraceFlags.sparks_sampled = enabled;
1761 enabled = rtsTrue;
1762 break;
1763 case 'f':
1764 RtsFlags.TraceFlags.sparks_full = enabled;
1765 enabled = rtsTrue;
1766 break;
1767 case 't':
1768 RtsFlags.TraceFlags.timestamp = enabled;
1769 enabled = rtsTrue;
1770 break;
1771 case 'g':
1772 RtsFlags.TraceFlags.gc = enabled;
1773 enabled = rtsTrue;
1774 break;
1775 case 'u':
1776 RtsFlags.TraceFlags.user = enabled;
1777 enabled = rtsTrue;
1778 break;
1779 default:
1780 errorBelch("unknown trace option: %c",*c);
1781 break;
1782 }
1783 }
1784 }
1785 #endif
1786
1787 static void GNU_ATTRIBUTE(__noreturn__)
1788 bad_option(const char *s)
1789 {
1790 errorBelch("bad RTS option: %s", s);
1791 stg_exit(EXIT_FAILURE);
1792 }
1793
1794 /* ----------------------------------------------------------------------------
1795 Copying and freeing argc/argv
1796 ------------------------------------------------------------------------- */
1797
1798 static char * copyArg(char *arg)
1799 {
1800 char *new_arg = stgMallocBytes(strlen(arg) + 1, "copyArg");
1801 strcpy(new_arg, arg);
1802 return new_arg;
1803 }
1804
1805 static char ** copyArgv(int argc, char *argv[])
1806 {
1807 int i;
1808 char **new_argv;
1809
1810 new_argv = stgCallocBytes(argc + 1, sizeof (char *), "copyArgv 1");
1811 for (i = 0; i < argc; i++) {
1812 new_argv[i] = copyArg(argv[i]);
1813 }
1814 new_argv[argc] = NULL;
1815 return new_argv;
1816 }
1817
1818 static void freeArgv(int argc, char *argv[])
1819 {
1820 int i;
1821 if (argv != NULL) {
1822 for (i = 0; i < argc; i++) {
1823 stgFree(argv[i]);
1824 }
1825 stgFree(argv);
1826 }
1827 }
1828
1829 /* -----------------------------------------------------------------------------
1830 Getting/Setting the program's arguments.
1831
1832 These are used by System.Environment, and parts of the RTS.
1833 -------------------------------------------------------------------------- */
1834
1835 void
1836 setProgName(char *argv[])
1837 {
1838 char *last_slash;
1839
1840 if (argv[0] == NULL) { // #7037
1841 prog_name = "";
1842 return;
1843 }
1844
1845 /* Remove directory from argv[0] -- default files in current directory */
1846 #if !defined(mingw32_HOST_OS)
1847 if ( (last_slash = (char *) strrchr(argv[0], '/')) != NULL ) {
1848 prog_name = last_slash+1;
1849 } else {
1850 prog_name = argv[0];
1851 }
1852 #else
1853 last_slash = argv[0] + (strlen(argv[0]) - 1);
1854 while ( last_slash > argv[0] ) {
1855 if ( *last_slash == '/' || *last_slash == '\\' ) {
1856 prog_name = last_slash+1;
1857 return;
1858 }
1859 last_slash--;
1860 }
1861 prog_name = argv[0];
1862 #endif
1863 }
1864
1865 void
1866 getProgArgv(int *argc, char **argv[])
1867 {
1868 if (argc) { *argc = prog_argc; }
1869 if (argv) { *argv = prog_argv; }
1870 }
1871
1872 void
1873 setProgArgv(int argc, char *argv[])
1874 {
1875 prog_argc = argc;
1876 prog_argv = copyArgv(argc,argv);
1877 setProgName(prog_argv);
1878 }
1879
1880 static void
1881 freeProgArgv(void)
1882 {
1883 freeArgv(prog_argc,prog_argv);
1884 prog_argc = 0;
1885 prog_argv = NULL;
1886 }
1887
1888 /* ----------------------------------------------------------------------------
1889 The full argv - a copy of the original program's argc/argv
1890 ------------------------------------------------------------------------- */
1891
1892 void
1893 setFullProgArgv(int argc, char *argv[])
1894 {
1895 full_prog_argc = argc;
1896 full_prog_argv = copyArgv(argc,argv);
1897 }
1898
1899 /* These functions record and recall the full arguments, including the
1900 +RTS ... -RTS options. The reason for adding them was so that the
1901 ghc-inplace program can pass /all/ the arguments on to the real ghc. */
1902 void
1903 getFullProgArgv(int *argc, char **argv[])
1904 {
1905 if (argc) { *argc = full_prog_argc; }
1906 if (argv) { *argv = full_prog_argv; }
1907 }
1908
1909 void
1910 freeFullProgArgv (void)
1911 {
1912 freeArgv(full_prog_argc, full_prog_argv);
1913 full_prog_argc = 0;
1914 full_prog_argv = NULL;
1915 }
1916
1917 /* ----------------------------------------------------------------------------
1918 The Win32 argv
1919 ------------------------------------------------------------------------- */
1920
1921 #if defined(mingw32_HOST_OS)
1922 void freeWin32ProgArgv (void);
1923
1924 void
1925 freeWin32ProgArgv (void)
1926 {
1927 int i;
1928
1929 if (win32_prog_argv != NULL) {
1930 for (i = 0; i < win32_prog_argc; i++) {
1931 stgFree(win32_prog_argv[i]);
1932 }
1933 stgFree(win32_prog_argv);
1934 }
1935
1936 win32_prog_argc = 0;
1937 win32_prog_argv = NULL;
1938 }
1939
1940 void
1941 getWin32ProgArgv(int *argc, wchar_t **argv[])
1942 {
1943 *argc = win32_prog_argc;
1944 *argv = win32_prog_argv;
1945 }
1946
1947 void
1948 setWin32ProgArgv(int argc, wchar_t *argv[])
1949 {
1950 int i;
1951
1952 freeWin32ProgArgv();
1953
1954 win32_prog_argc = argc;
1955 if (argv == NULL) {
1956 win32_prog_argv = NULL;
1957 return;
1958 }
1959
1960 win32_prog_argv = stgCallocBytes(argc + 1, sizeof (wchar_t *),
1961 "setWin32ProgArgv 1");
1962 for (i = 0; i < argc; i++) {
1963 win32_prog_argv[i] = stgMallocBytes((wcslen(argv[i]) + 1) * sizeof(wchar_t),
1964 "setWin32ProgArgv 2");
1965 wcscpy(win32_prog_argv[i], argv[i]);
1966 }
1967 win32_prog_argv[argc] = NULL;
1968 }
1969 #endif
1970
1971 /* ----------------------------------------------------------------------------
1972 The RTS argv
1973 ------------------------------------------------------------------------- */
1974
1975 static void
1976 freeRtsArgv(void)
1977 {
1978 freeArgv(rts_argc,rts_argv);
1979 rts_argc = 0;
1980 rts_argv = NULL;
1981 rts_argv_size = 0;
1982 }
1983
1984 /* ----------------------------------------------------------------------------
1985 All argvs
1986 ------------------------------------------------------------------------- */
1987
1988 void freeRtsArgs(void)
1989 {
1990 #if defined(mingw32_HOST_OS)
1991 freeWin32ProgArgv();
1992 #endif
1993 freeFullProgArgv();
1994 freeProgArgv();
1995 freeRtsArgv();
1996 }
1997
1998
1999 /*
2000 Note [OPTION_SAFE vs OPTION_UNSAFE]
2001
2002 Ticket #3910 originally pointed out that the RTS options are a potential
2003 security problem. For example the -t -s or -S flags can be used to
2004 overwrite files. This would be bad in the context of CGI scripts or
2005 setuid binaries. So we introduced a system where +RTS processing is more
2006 or less disabled unless you pass the -rtsopts flag at link time.
2007
2008 This scheme is safe enough but it also really annoyes users. They have
2009 to use -rtsopts in many circumstances: with -threaded to use -N, with
2010 -eventlog to use -l, with -prof to use any of the profiling flags. Many
2011 users just set -rtsopts globally or in project .cabal files. Apart from
2012 annoying users it reduces security because it means that deployed
2013 binaries will have all RTS options enabled rather than just profiling
2014 ones.
2015
2016 So now, we relax the set of RTS options that are available in the
2017 default -rtsopts=some case. For "deployment" ways like vanilla and
2018 -threaded we remain quite conservative. Only --info -? --help are
2019 allowed for vanilla. For -threaded, -N and -N<x> are allowed with a
2020 check that x <= num cpus.
2021
2022 For "developer" ways like -debug, -eventlog, -prof, we allow all the
2023 options that are special to that way. Some of these allow writing files,
2024 but the file written is not directly under the control of the attacker.
2025 For the setuid case (where the attacker would have control over binary
2026 name, current dir, local symlinks etc) we check if the process is
2027 running setuid/setgid and refuse all RTS option processing. Users would
2028 need to use -rtsopts=all in this case.
2029
2030 We are making the assumption that developers will not deploy binaries
2031 built in the -debug, -eventlog, -prof ways. And even if they do, the
2032 damage should be limited to DOS, information disclosure and writing
2033 files like <progname>.eventlog, not arbitrary files.
2034 */