1ec5db0bb04b31cd28e313de866b6ea2e671cc34
[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, uint32_t 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 uint32_t mode;
495 uint32_t total_arg;
496 uint32_t 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 = (uint32_t)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 = (uint32_t)
904 (decodeSize(rts_argv[arg], 2, BLOCK_SIZE, HS_WORD_MAX)
905 / BLOCK_SIZE);
906 }
907 break;
908
909 case 'O':
910 OPTION_UNSAFE;
911 RtsFlags.GcFlags.minOldGenSize =
912 (uint32_t)(decodeSize(rts_argv[arg], 2, BLOCK_SIZE,
913 HS_WORD_MAX)
914 / BLOCK_SIZE);
915 break;
916
917 case 'I': /* idle GC delay */
918 OPTION_UNSAFE;
919 if (rts_argv[arg][2] == '\0') {
920 /* use default */
921 } else {
922 Time t = fsecondsToTime(atof(rts_argv[arg]+2));
923 if (t == 0) {
924 RtsFlags.GcFlags.doIdleGC = rtsFalse;
925 } else {
926 RtsFlags.GcFlags.doIdleGC = rtsTrue;
927 RtsFlags.GcFlags.idleGCDelayTime = t;
928 }
929 }
930 break;
931
932 case 'T':
933 OPTION_SAFE;
934 RtsFlags.GcFlags.giveStats = COLLECT_GC_STATS;
935 unchecked_arg_start++;
936 goto check_rest; /* Don't initialize statistics file. */
937
938 case 'S':
939 OPTION_SAFE; /* but see below */
940 RtsFlags.GcFlags.giveStats = VERBOSE_GC_STATS;
941 goto stats;
942
943 case 's':
944 OPTION_SAFE; /* but see below */
945 RtsFlags.GcFlags.giveStats = SUMMARY_GC_STATS;
946 goto stats;
947
948 case 't':
949 OPTION_SAFE; /* but see below */
950 RtsFlags.GcFlags.giveStats = ONELINE_GC_STATS;
951 goto stats;
952
953 stats:
954 {
955 int r;
956 if (rts_argv[arg][2] != '\0') {
957 OPTION_UNSAFE;
958 }
959 r = openStatsFile(rts_argv[arg]+2, NULL,
960 &RtsFlags.GcFlags.statsFile);
961 if (r == -1) { error = rtsTrue; }
962 }
963 break;
964
965 case 'Z':
966 OPTION_UNSAFE;
967 RtsFlags.GcFlags.squeezeUpdFrames = rtsFalse;
968 unchecked_arg_start++;
969 goto check_rest;
970
971 /* =========== PROFILING ========================== */
972
973 case 'P': /* detailed cost centre profiling (time/alloc) */
974 case 'p': /* cost centre profiling (time/alloc) */
975 OPTION_SAFE;
976 PROFILING_BUILD_ONLY(
977 switch (rts_argv[arg][2]) {
978 case 'a':
979 RtsFlags.CcFlags.doCostCentres = COST_CENTRES_ALL;
980 if (rts_argv[arg][3] != '\0') {
981 errorBelch("flag -Pa given an argument"
982 " when none was expected: %s"
983 ,rts_argv[arg]);
984 error = rtsTrue;
985 }
986 break;
987 case '\0':
988 if (rts_argv[arg][1] == 'P') {
989 RtsFlags.CcFlags.doCostCentres =
990 COST_CENTRES_VERBOSE;
991 } else {
992 RtsFlags.CcFlags.doCostCentres =
993 COST_CENTRES_SUMMARY;
994 }
995 break;
996 default:
997 unchecked_arg_start++;
998 goto check_rest;
999 }
1000 ) break;
1001
1002 case 'R':
1003 OPTION_SAFE;
1004 PROFILING_BUILD_ONLY(
1005 RtsFlags.ProfFlags.maxRetainerSetSize =
1006 atof(rts_argv[arg]+2);
1007 ) break;
1008 case 'L':
1009 OPTION_SAFE;
1010 PROFILING_BUILD_ONLY(
1011 RtsFlags.ProfFlags.ccsLength = atof(rts_argv[arg]+2);
1012 if(RtsFlags.ProfFlags.ccsLength <= 0) {
1013 bad_option(rts_argv[arg]);
1014 }
1015 ) break;
1016 case 'h': /* serial heap profile */
1017 #if !defined(PROFILING)
1018 switch (rts_argv[arg][2]) {
1019 case '\0':
1020 case 'T':
1021 OPTION_UNSAFE;
1022 RtsFlags.ProfFlags.doHeapProfile = HEAP_BY_CLOSURE_TYPE;
1023 break;
1024 default:
1025 OPTION_SAFE;
1026 PROFILING_BUILD_ONLY();
1027 }
1028 #else
1029 OPTION_SAFE;
1030 PROFILING_BUILD_ONLY(
1031 error = read_heap_profiling_flag(rts_argv[arg]);
1032 );
1033 #endif /* PROFILING */
1034 break;
1035
1036 case 'i': /* heap sample interval */
1037 OPTION_UNSAFE;
1038 if (rts_argv[arg][2] == '\0') {
1039 /* use default */
1040 } else {
1041 RtsFlags.ProfFlags.heapProfileInterval =
1042 fsecondsToTime(atof(rts_argv[arg]+2));
1043 }
1044 break;
1045
1046 /* =========== CONCURRENT ========================= */
1047 case 'C': /* context switch interval */
1048 OPTION_UNSAFE;
1049 if (rts_argv[arg][2] == '\0')
1050 RtsFlags.ConcFlags.ctxtSwitchTime = 0;
1051 else {
1052 RtsFlags.ConcFlags.ctxtSwitchTime =
1053 fsecondsToTime(atof(rts_argv[arg]+2));
1054 }
1055 break;
1056
1057 case 'V': /* master tick interval */
1058 OPTION_UNSAFE;
1059 if (rts_argv[arg][2] == '\0') {
1060 // turns off ticks completely
1061 RtsFlags.MiscFlags.tickInterval = 0;
1062 } else {
1063 RtsFlags.MiscFlags.tickInterval =
1064 fsecondsToTime(atof(rts_argv[arg]+2));
1065 }
1066 break;
1067
1068 #if !defined(NOSMP)
1069 case 'N':
1070 OPTION_SAFE;
1071 THREADED_BUILD_ONLY(
1072 if (rts_argv[arg][2] == '\0') {
1073 #if defined(PROFILING)
1074 RtsFlags.ParFlags.nNodes = 1;
1075 #else
1076 RtsFlags.ParFlags.nNodes = getNumberOfProcessors();
1077 #endif
1078 } else {
1079 int nNodes;
1080 OPTION_SAFE; /* but see extra checks below... */
1081
1082 nNodes = strtol(rts_argv[arg]+2, (char **) NULL, 10);
1083
1084 if (nNodes <= 0) {
1085 errorBelch("bad value for -N");
1086 error = rtsTrue;
1087 }
1088 if (rtsOptsEnabled == RtsOptsSafeOnly &&
1089 nNodes > (int)getNumberOfProcessors()) {
1090 errorRtsOptsDisabled("Using large values for -N is not allowed by default. %s");
1091 stg_exit(EXIT_FAILURE);
1092 }
1093 RtsFlags.ParFlags.nNodes = (uint32_t)nNodes;
1094 }
1095 ) break;
1096
1097 case 'g':
1098 OPTION_UNSAFE;
1099 THREADED_BUILD_ONLY(
1100 switch (rts_argv[arg][2]) {
1101 case '1':
1102 // backwards compat only
1103 RtsFlags.ParFlags.parGcEnabled = rtsFalse;
1104 break;
1105 default:
1106 errorBelch("unknown RTS option: %s",rts_argv[arg]);
1107 error = rtsTrue;
1108 break;
1109 }
1110 ) break;
1111
1112 case 'q':
1113 OPTION_UNSAFE;
1114 THREADED_BUILD_ONLY(
1115 switch (rts_argv[arg][2]) {
1116 case '\0':
1117 errorBelch("incomplete RTS option: %s",rts_argv[arg]);
1118 error = rtsTrue;
1119 break;
1120 case 'g':
1121 if (rts_argv[arg][3] == '\0') {
1122 RtsFlags.ParFlags.parGcEnabled = rtsFalse;
1123 } else {
1124 RtsFlags.ParFlags.parGcEnabled = rtsTrue;
1125 RtsFlags.ParFlags.parGcGen
1126 = strtol(rts_argv[arg]+3, (char **) NULL, 10);
1127 }
1128 break;
1129 case 'b':
1130 if (rts_argv[arg][3] == '\0') {
1131 RtsFlags.ParFlags.parGcLoadBalancingEnabled =
1132 rtsFalse;
1133 }
1134 else {
1135 RtsFlags.ParFlags.parGcLoadBalancingEnabled =
1136 rtsTrue;
1137 RtsFlags.ParFlags.parGcLoadBalancingGen
1138 = strtol(rts_argv[arg]+3, (char **) NULL, 10);
1139 }
1140 break;
1141 case 'i':
1142 RtsFlags.ParFlags.parGcNoSyncWithIdle
1143 = strtol(rts_argv[arg]+3, (char **) NULL, 10);
1144 break;
1145 case 'n': {
1146 int threads;
1147 threads = strtol(rts_argv[arg]+3, (char **) NULL, 10);
1148 if (threads <= 0) {
1149 errorBelch("-qn must be 1 or greater");
1150 error = rtsTrue;
1151 } else {
1152 RtsFlags.ParFlags.parGcThreads = threads;
1153 }
1154 break;
1155 }
1156 case 'a':
1157 RtsFlags.ParFlags.setAffinity = rtsTrue;
1158 break;
1159 case 'm':
1160 RtsFlags.ParFlags.migrate = rtsFalse;
1161 break;
1162 case 'w':
1163 // -qw was removed; accepted for backwards compat
1164 break;
1165 default:
1166 errorBelch("unknown RTS option: %s",rts_argv[arg]);
1167 error = rtsTrue;
1168 break;
1169 }
1170 ) break;
1171 #endif
1172 /* =========== PARALLEL =========================== */
1173 case 'e':
1174 OPTION_UNSAFE;
1175 THREADED_BUILD_ONLY(
1176 if (rts_argv[arg][2] != '\0') {
1177 RtsFlags.ParFlags.maxLocalSparks
1178 = strtol(rts_argv[arg]+2, (char **) NULL, 10);
1179 if (RtsFlags.ParFlags.maxLocalSparks <= 0) {
1180 errorBelch("bad value for -e");
1181 error = rtsTrue;
1182 }
1183 }
1184 ) break;
1185
1186 /* =========== TICKY ============================== */
1187
1188 case 'r': /* Basic profiling stats */
1189 OPTION_SAFE;
1190 TICKY_BUILD_ONLY(
1191
1192 RtsFlags.TickyFlags.showTickyStats = rtsTrue;
1193
1194 {
1195 int r;
1196 if (rts_argv[arg][2] != '\0') {
1197 OPTION_UNSAFE;
1198 }
1199 r = openStatsFile(rts_argv[arg]+2,
1200 TICKY_FILENAME_FMT,
1201 &RtsFlags.TickyFlags.tickyFile);
1202 if (r == -1) { error = rtsTrue; }
1203 }
1204 ) break;
1205
1206 /* =========== TRACING ---------=================== */
1207
1208 case 'l':
1209 OPTION_SAFE;
1210 TRACING_BUILD_ONLY(
1211 RtsFlags.TraceFlags.tracing = TRACE_EVENTLOG;
1212 read_trace_flags(&rts_argv[arg][2]);
1213 );
1214 break;
1215
1216 case 'v':
1217 OPTION_SAFE;
1218 DEBUG_BUILD_ONLY(
1219 RtsFlags.TraceFlags.tracing = TRACE_STDERR;
1220 read_trace_flags(&rts_argv[arg][2]);
1221 );
1222 break;
1223
1224 /* =========== EXTENDED OPTIONS =================== */
1225
1226 case 'x': /* Extend the argument space */
1227 unchecked_arg_start++;
1228 switch(rts_argv[arg][2]) {
1229 case '\0':
1230 OPTION_SAFE;
1231 errorBelch("incomplete RTS option: %s",rts_argv[arg]);
1232 error = rtsTrue;
1233 break;
1234
1235 case 'b': /* heapBase in hex; undocumented */
1236 OPTION_UNSAFE;
1237 if (rts_argv[arg][3] != '\0') {
1238 RtsFlags.GcFlags.heapBase
1239 = strtol(rts_argv[arg]+3, (char **) NULL, 16);
1240 } else {
1241 errorBelch("-xb: requires argument");
1242 error = rtsTrue;
1243 }
1244 break;
1245
1246 #if defined(x86_64_HOST_ARCH)
1247 case 'm': /* linkerMemBase */
1248 OPTION_UNSAFE;
1249 if (rts_argv[arg][3] != '\0') {
1250 RtsFlags.MiscFlags.linkerMemBase
1251 = strtol(rts_argv[arg]+3, (char **) NULL, 16);
1252 if (RtsFlags.MiscFlags.linkerMemBase > 0x80000000) {
1253 errorBelch("-xm: value must be <80000000");
1254 error = rtsTrue;
1255 }
1256 } else {
1257 RtsFlags.MiscFlags.linkerMemBase = 0;
1258 }
1259 break;
1260 #endif
1261
1262 case 'c': /* Debugging tool: show current cost centre on
1263 an exception */
1264 OPTION_SAFE;
1265 PROFILING_BUILD_ONLY(
1266 RtsFlags.ProfFlags.showCCSOnException = rtsTrue;
1267 );
1268 unchecked_arg_start++;
1269 goto check_rest;
1270
1271 case 't': /* Include memory used by TSOs in a heap profile */
1272 OPTION_SAFE;
1273 PROFILING_BUILD_ONLY(
1274 RtsFlags.ProfFlags.includeTSOs = rtsTrue;
1275 );
1276 unchecked_arg_start++;
1277 goto check_rest;
1278
1279 /*
1280 * The option prefix '-xx' is reserved for future
1281 * extension. KSW 1999-11.
1282 */
1283
1284 case 'q':
1285 OPTION_UNSAFE;
1286 RtsFlags.GcFlags.allocLimitGrace
1287 = decodeSize(rts_argv[arg], 3, BLOCK_SIZE, HS_INT_MAX)
1288 / BLOCK_SIZE;
1289 break;
1290
1291 default:
1292 OPTION_SAFE;
1293 errorBelch("unknown RTS option: %s",rts_argv[arg]);
1294 error = rtsTrue;
1295 break;
1296 }
1297 break; /* defensive programming */
1298
1299 /* check the rest to be sure there is nothing afterwards.*/
1300 /* see Trac #9839 */
1301 check_rest:
1302 {
1303 /* start checking from the first unchecked position,
1304 * not from index 2*/
1305 /* see Trac #9839 */
1306 if (rts_argv[arg][unchecked_arg_start] != '\0') {
1307 errorBelch("flag -%c given an argument"
1308 " when none was expected: %s",
1309 rts_argv[arg][1],rts_argv[arg]);
1310 error = rtsTrue;
1311 }
1312 break;
1313 }
1314
1315 /* =========== OH DEAR ============================ */
1316 default:
1317 OPTION_SAFE;
1318 errorBelch("unknown RTS option: %s",rts_argv[arg]);
1319 error = rtsTrue;
1320 break;
1321 }
1322
1323 if (!option_checked) {
1324 /* Naughty! Someone didn't use OPTION_UNSAFE / OPTION_SAFE for
1325 an option above */
1326 errorBelch("Internal error in the RTS options parser");
1327 stg_exit(EXIT_FAILURE);
1328 }
1329 }
1330 }
1331
1332 if (error) errorUsage();
1333 }
1334
1335 /* -----------------------------------------------------------------------------
1336 * normaliseRtsOpts: Set some derived values, and make sure things are
1337 * within sensible ranges.
1338 * -------------------------------------------------------------------------- */
1339
1340 static void normaliseRtsOpts (void)
1341 {
1342 if (RtsFlags.MiscFlags.tickInterval < 0) {
1343 RtsFlags.MiscFlags.tickInterval = DEFAULT_TICK_INTERVAL;
1344 }
1345
1346 // If the master timer is disabled, turn off the other timers.
1347 if (RtsFlags.MiscFlags.tickInterval == 0) {
1348 RtsFlags.ConcFlags.ctxtSwitchTime = 0;
1349 RtsFlags.GcFlags.idleGCDelayTime = 0;
1350 RtsFlags.ProfFlags.heapProfileInterval = 0;
1351 }
1352
1353 // Determine what tick interval we should use for the RTS timer
1354 // by taking the shortest of the various intervals that we need to
1355 // monitor.
1356 if (RtsFlags.ConcFlags.ctxtSwitchTime > 0) {
1357 RtsFlags.MiscFlags.tickInterval =
1358 stg_min(RtsFlags.ConcFlags.ctxtSwitchTime,
1359 RtsFlags.MiscFlags.tickInterval);
1360 }
1361
1362 if (RtsFlags.GcFlags.idleGCDelayTime > 0) {
1363 RtsFlags.MiscFlags.tickInterval =
1364 stg_min(RtsFlags.GcFlags.idleGCDelayTime,
1365 RtsFlags.MiscFlags.tickInterval);
1366 }
1367
1368 if (RtsFlags.ProfFlags.heapProfileInterval > 0) {
1369 RtsFlags.MiscFlags.tickInterval =
1370 stg_min(RtsFlags.ProfFlags.heapProfileInterval,
1371 RtsFlags.MiscFlags.tickInterval);
1372 }
1373
1374 if (RtsFlags.ConcFlags.ctxtSwitchTime > 0) {
1375 RtsFlags.ConcFlags.ctxtSwitchTicks =
1376 RtsFlags.ConcFlags.ctxtSwitchTime /
1377 RtsFlags.MiscFlags.tickInterval;
1378 } else {
1379 RtsFlags.ConcFlags.ctxtSwitchTicks = 0;
1380 }
1381
1382 if (RtsFlags.ProfFlags.heapProfileInterval > 0) {
1383 RtsFlags.ProfFlags.heapProfileIntervalTicks =
1384 RtsFlags.ProfFlags.heapProfileInterval /
1385 RtsFlags.MiscFlags.tickInterval;
1386 } else {
1387 RtsFlags.ProfFlags.heapProfileIntervalTicks = 0;
1388 }
1389
1390 if (RtsFlags.GcFlags.stkChunkBufferSize >
1391 RtsFlags.GcFlags.stkChunkSize / 2) {
1392 errorBelch("stack chunk buffer size (-kb) must be less than 50%%\n"
1393 "of the stack chunk size (-kc)");
1394 errorUsage();
1395 }
1396
1397 #ifdef THREADED_RTS
1398 if (RtsFlags.ParFlags.parGcThreads > RtsFlags.ParFlags.nNodes) {
1399 errorBelch("GC threads (-qn) must be between 1 and the value of -N");
1400 errorUsage();
1401 }
1402 #endif
1403 }
1404
1405 static void errorUsage (void)
1406 {
1407 const char **p;
1408
1409 fflush(stdout);
1410 for (p = usage_text; *p; p++)
1411 errorBelch("%s", *p);
1412 stg_exit(EXIT_FAILURE);
1413 }
1414
1415 static void
1416 stats_fprintf(FILE *f, char *s, ...)
1417 {
1418 va_list ap;
1419 va_start(ap,s);
1420 if (f == NULL) {
1421 vdebugBelch(s, ap);
1422 } else {
1423 vfprintf(f, s, ap);
1424 }
1425 va_end(ap);
1426 }
1427
1428 /* -----------------------------------------------------------------------------
1429 * openStatsFile: open a file in which to put some runtime stats
1430 * -------------------------------------------------------------------------- */
1431
1432 static int // return -1 on error
1433 openStatsFile (char *filename, // filename, or NULL
1434 const char *filename_fmt, // if filename == NULL, use
1435 // this fmt with sprintf to
1436 // generate the filename. %s
1437 // expands to the program name.
1438 FILE **file_ret) // return the FILE*
1439 {
1440 FILE *f = NULL;
1441
1442 if (strequal(filename, "stderr")
1443 || (filename_fmt == NULL && *filename == '\0')) {
1444 f = NULL; /* NULL means use debugBelch */
1445 } else {
1446 if (*filename != '\0') { /* stats file specified */
1447 f = fopen(filename,"w");
1448 } else {
1449 /* default <program>.<ext> */
1450 char stats_filename[STATS_FILENAME_MAXLEN];
1451 sprintf(stats_filename, filename_fmt, prog_name);
1452 f = fopen(stats_filename,"w");
1453 }
1454 if (f == NULL) {
1455 errorBelch("Can't open stats file %s\n", filename);
1456 return -1;
1457 }
1458 }
1459 *file_ret = f;
1460
1461 return 0;
1462 }
1463
1464 /* -----------------------------------------------------------------------------
1465 * initStatsFile: write a line to the file containing the program name
1466 * and the arguments it was invoked with.
1467 -------------------------------------------------------------------------- */
1468
1469 static void initStatsFile (FILE *f)
1470 {
1471 /* Write prog_argv and rts_argv into start of stats file */
1472 int count;
1473 for (count = 0; count < prog_argc; count++) {
1474 stats_fprintf(f, "%s ", prog_argv[count]);
1475 }
1476 stats_fprintf(f, "+RTS ");
1477 for (count = 0; count < rts_argc; count++)
1478 stats_fprintf(f, "%s ", rts_argv[count]);
1479 stats_fprintf(f, "\n");
1480 }
1481
1482 /* -----------------------------------------------------------------------------
1483 * decodeSize: parse a string containing a size, like 300K or 1.2M
1484 -------------------------------------------------------------------------- */
1485
1486 static StgWord64
1487 decodeSize(const char *flag, uint32_t offset, StgWord64 min, StgWord64 max)
1488 {
1489 char c;
1490 const char *s;
1491 StgDouble m;
1492 StgWord64 val;
1493
1494 s = flag + offset;
1495
1496 if (!*s)
1497 {
1498 m = 0;
1499 }
1500 else
1501 {
1502 m = atof(s);
1503 c = s[strlen(s)-1];
1504
1505 if (c == 'g' || c == 'G')
1506 m *= 1024*1024*1024;
1507 else if (c == 'm' || c == 'M')
1508 m *= 1024*1024;
1509 else if (c == 'k' || c == 'K')
1510 m *= 1024;
1511 else if (c == 'w' || c == 'W')
1512 m *= sizeof(W_);
1513 }
1514
1515 val = (StgWord64)m;
1516
1517 if (m < 0 || val < min || val > max) {
1518 // printf doesn't like 64-bit format specs on Windows
1519 // apparently, so fall back to unsigned long.
1520 errorBelch("error in RTS option %s: size outside allowed range (%" FMT_Word " - %" FMT_Word ")", flag, (W_)min, (W_)max);
1521 stg_exit(EXIT_FAILURE);
1522 }
1523
1524 return val;
1525 }
1526
1527 #ifdef DEBUG
1528 static void read_debug_flags(const char* arg)
1529 {
1530 // Already parsed "-D"
1531 const char *c;
1532 for (c = arg + 2; *c != '\0'; c++) {
1533 switch (*c) {
1534 case 's':
1535 RtsFlags.DebugFlags.scheduler = rtsTrue;
1536 break;
1537 case 'i':
1538 RtsFlags.DebugFlags.interpreter = rtsTrue;
1539 break;
1540 case 'w':
1541 RtsFlags.DebugFlags.weak = rtsTrue;
1542 break;
1543 case 'G':
1544 RtsFlags.DebugFlags.gccafs = rtsTrue;
1545 break;
1546 case 'g':
1547 RtsFlags.DebugFlags.gc = rtsTrue;
1548 break;
1549 case 'b':
1550 RtsFlags.DebugFlags.block_alloc = rtsTrue;
1551 break;
1552 case 'S':
1553 RtsFlags.DebugFlags.sanity = rtsTrue;
1554 break;
1555 case 't':
1556 RtsFlags.DebugFlags.stable = rtsTrue;
1557 break;
1558 case 'p':
1559 RtsFlags.DebugFlags.prof = rtsTrue;
1560 break;
1561 case 'l':
1562 RtsFlags.DebugFlags.linker = rtsTrue;
1563 break;
1564 case 'a':
1565 RtsFlags.DebugFlags.apply = rtsTrue;
1566 break;
1567 case 'm':
1568 RtsFlags.DebugFlags.stm = rtsTrue;
1569 break;
1570 case 'z':
1571 RtsFlags.DebugFlags.squeeze = rtsTrue;
1572 break;
1573 case 'c':
1574 RtsFlags.DebugFlags.hpc = rtsTrue;
1575 break;
1576 case 'r':
1577 RtsFlags.DebugFlags.sparks = rtsTrue;
1578 break;
1579 default:
1580 bad_option( arg );
1581 }
1582 }
1583 // -Dx also turns on -v. Use -l to direct trace
1584 // events to the .eventlog file instead.
1585 RtsFlags.TraceFlags.tracing = TRACE_STDERR;
1586 }
1587 #endif
1588
1589 #ifdef PROFILING
1590 // Parse a "-h" flag, returning whether the parse resulted in an error.
1591 static rtsBool read_heap_profiling_flag(const char *arg)
1592 {
1593 // Already parsed "-h"
1594
1595 rtsBool error = rtsFalse;
1596 switch (arg[2]) {
1597 case '\0':
1598 case 'C':
1599 case 'c':
1600 case 'M':
1601 case 'm':
1602 case 'D':
1603 case 'd':
1604 case 'Y':
1605 case 'y':
1606 case 'R':
1607 case 'r':
1608 case 'B':
1609 case 'b':
1610 if (arg[2] != '\0' && arg[3] != '\0') {
1611 {
1612 const char *left = strchr(arg, '{');
1613 const char *right = strrchr(arg, '}');
1614
1615 // curly braces are optional, for
1616 // backwards compat.
1617 if (left)
1618 left = left+1;
1619 else
1620 left = arg + 3;
1621
1622 if (!right)
1623 right = arg + strlen(arg);
1624
1625 char *selector = stgStrndup(left, right - left + 1);
1626
1627 switch (arg[2]) {
1628 case 'c': // cost centre label select
1629 RtsFlags.ProfFlags.ccSelector = selector;
1630 break;
1631 case 'C':
1632 RtsFlags.ProfFlags.ccsSelector = selector;
1633 break;
1634 case 'M':
1635 case 'm': // cost centre module select
1636 RtsFlags.ProfFlags.modSelector = selector;
1637 break;
1638 case 'D':
1639 case 'd': // closure descr select
1640 RtsFlags.ProfFlags.descrSelector = selector;
1641 break;
1642 case 'Y':
1643 case 'y': // closure type select
1644 RtsFlags.ProfFlags.typeSelector = selector;
1645 break;
1646 case 'R':
1647 case 'r': // retainer select
1648 RtsFlags.ProfFlags.retainerSelector = selector;
1649 break;
1650 case 'B':
1651 case 'b': // biography select
1652 RtsFlags.ProfFlags.bioSelector = selector;
1653 break;
1654 default:
1655 free(selector);
1656 }
1657 }
1658 break;
1659 }
1660
1661 if (RtsFlags.ProfFlags.doHeapProfile != 0) {
1662 errorBelch("multiple heap profile options");
1663 error = rtsTrue;
1664 break;
1665 }
1666
1667 switch (arg[2]) {
1668 case '\0':
1669 case 'C':
1670 case 'c':
1671 RtsFlags.ProfFlags.doHeapProfile = HEAP_BY_CCS;
1672 break;
1673 case 'M':
1674 case 'm':
1675 RtsFlags.ProfFlags.doHeapProfile = HEAP_BY_MOD;
1676 break;
1677 case 'D':
1678 case 'd':
1679 RtsFlags.ProfFlags.doHeapProfile = HEAP_BY_DESCR;
1680 break;
1681 case 'Y':
1682 case 'y':
1683 RtsFlags.ProfFlags.doHeapProfile = HEAP_BY_TYPE;
1684 break;
1685 case 'R':
1686 case 'r':
1687 RtsFlags.ProfFlags.doHeapProfile = HEAP_BY_RETAINER;
1688 break;
1689 case 'B':
1690 case 'b':
1691 RtsFlags.ProfFlags.doHeapProfile = HEAP_BY_LDV;
1692 break;
1693 }
1694 break;
1695
1696 default:
1697 errorBelch("invalid heap profile option: %s", arg);
1698 error = rtsTrue;
1699 }
1700
1701 return error;
1702 }
1703 #endif
1704
1705 #if defined(TRACING)
1706 static void read_trace_flags(const char *arg)
1707 {
1708 const char *c;
1709 rtsBool enabled = rtsTrue;
1710 /* Syntax for tracing flags currently looks like:
1711 *
1712 * -l To turn on eventlog tracing with default trace classes
1713 * -lx Turn on class 'x' (for some class listed below)
1714 * -l-x Turn off class 'x'
1715 * -la Turn on all classes
1716 * -l-a Turn off all classes
1717 *
1718 * This lets users say things like:
1719 * -la-p "all but sparks"
1720 * -l-ap "only sparks"
1721 */
1722
1723 /* Start by turning on the default tracing flags.
1724 *
1725 * Currently this is all the trace classes, except full-detail sparks.
1726 * Similarly, in future we might default to slightly less verbose
1727 * scheduler or GC tracing.
1728 */
1729 RtsFlags.TraceFlags.scheduler = rtsTrue;
1730 RtsFlags.TraceFlags.gc = rtsTrue;
1731 RtsFlags.TraceFlags.sparks_sampled = rtsTrue;
1732 RtsFlags.TraceFlags.user = rtsTrue;
1733
1734 for (c = arg; *c != '\0'; c++) {
1735 switch(*c) {
1736 case '\0':
1737 break;
1738 case '-':
1739 enabled = rtsFalse;
1740 break;
1741 case 'a':
1742 RtsFlags.TraceFlags.scheduler = enabled;
1743 RtsFlags.TraceFlags.gc = enabled;
1744 RtsFlags.TraceFlags.sparks_sampled = enabled;
1745 RtsFlags.TraceFlags.sparks_full = enabled;
1746 RtsFlags.TraceFlags.user = enabled;
1747 enabled = rtsTrue;
1748 break;
1749
1750 case 's':
1751 RtsFlags.TraceFlags.scheduler = enabled;
1752 enabled = rtsTrue;
1753 break;
1754 case 'p':
1755 RtsFlags.TraceFlags.sparks_sampled = enabled;
1756 enabled = rtsTrue;
1757 break;
1758 case 'f':
1759 RtsFlags.TraceFlags.sparks_full = enabled;
1760 enabled = rtsTrue;
1761 break;
1762 case 't':
1763 RtsFlags.TraceFlags.timestamp = enabled;
1764 enabled = rtsTrue;
1765 break;
1766 case 'g':
1767 RtsFlags.TraceFlags.gc = enabled;
1768 enabled = rtsTrue;
1769 break;
1770 case 'u':
1771 RtsFlags.TraceFlags.user = enabled;
1772 enabled = rtsTrue;
1773 break;
1774 default:
1775 errorBelch("unknown trace option: %c",*c);
1776 break;
1777 }
1778 }
1779 }
1780 #endif
1781
1782 static void GNU_ATTRIBUTE(__noreturn__)
1783 bad_option(const char *s)
1784 {
1785 errorBelch("bad RTS option: %s", s);
1786 stg_exit(EXIT_FAILURE);
1787 }
1788
1789 /* ----------------------------------------------------------------------------
1790 Copying and freeing argc/argv
1791 ------------------------------------------------------------------------- */
1792
1793 static char * copyArg(char *arg)
1794 {
1795 char *new_arg = stgMallocBytes(strlen(arg) + 1, "copyArg");
1796 strcpy(new_arg, arg);
1797 return new_arg;
1798 }
1799
1800 static char ** copyArgv(int argc, char *argv[])
1801 {
1802 int i;
1803 char **new_argv;
1804
1805 new_argv = stgCallocBytes(argc + 1, sizeof (char *), "copyArgv 1");
1806 for (i = 0; i < argc; i++) {
1807 new_argv[i] = copyArg(argv[i]);
1808 }
1809 new_argv[argc] = NULL;
1810 return new_argv;
1811 }
1812
1813 static void freeArgv(int argc, char *argv[])
1814 {
1815 int i;
1816 if (argv != NULL) {
1817 for (i = 0; i < argc; i++) {
1818 stgFree(argv[i]);
1819 }
1820 stgFree(argv);
1821 }
1822 }
1823
1824 /* -----------------------------------------------------------------------------
1825 Getting/Setting the program's arguments.
1826
1827 These are used by System.Environment, and parts of the RTS.
1828 -------------------------------------------------------------------------- */
1829
1830 void
1831 setProgName(char *argv[])
1832 {
1833 char *last_slash;
1834
1835 if (argv[0] == NULL) { // #7037
1836 prog_name = "";
1837 return;
1838 }
1839
1840 /* Remove directory from argv[0] -- default files in current directory */
1841 #if !defined(mingw32_HOST_OS)
1842 if ( (last_slash = (char *) strrchr(argv[0], '/')) != NULL ) {
1843 prog_name = last_slash+1;
1844 } else {
1845 prog_name = argv[0];
1846 }
1847 #else
1848 last_slash = argv[0] + (strlen(argv[0]) - 1);
1849 while ( last_slash > argv[0] ) {
1850 if ( *last_slash == '/' || *last_slash == '\\' ) {
1851 prog_name = last_slash+1;
1852 return;
1853 }
1854 last_slash--;
1855 }
1856 prog_name = argv[0];
1857 #endif
1858 }
1859
1860 void
1861 getProgArgv(int *argc, char **argv[])
1862 {
1863 if (argc) { *argc = prog_argc; }
1864 if (argv) { *argv = prog_argv; }
1865 }
1866
1867 void
1868 setProgArgv(int argc, char *argv[])
1869 {
1870 prog_argc = argc;
1871 prog_argv = copyArgv(argc,argv);
1872 setProgName(prog_argv);
1873 }
1874
1875 static void
1876 freeProgArgv(void)
1877 {
1878 freeArgv(prog_argc,prog_argv);
1879 prog_argc = 0;
1880 prog_argv = NULL;
1881 }
1882
1883 /* ----------------------------------------------------------------------------
1884 The full argv - a copy of the original program's argc/argv
1885 ------------------------------------------------------------------------- */
1886
1887 void
1888 setFullProgArgv(int argc, char *argv[])
1889 {
1890 full_prog_argc = argc;
1891 full_prog_argv = copyArgv(argc,argv);
1892 }
1893
1894 /* These functions record and recall the full arguments, including the
1895 +RTS ... -RTS options. The reason for adding them was so that the
1896 ghc-inplace program can pass /all/ the arguments on to the real ghc. */
1897 void
1898 getFullProgArgv(int *argc, char **argv[])
1899 {
1900 if (argc) { *argc = full_prog_argc; }
1901 if (argv) { *argv = full_prog_argv; }
1902 }
1903
1904 void
1905 freeFullProgArgv (void)
1906 {
1907 freeArgv(full_prog_argc, full_prog_argv);
1908 full_prog_argc = 0;
1909 full_prog_argv = NULL;
1910 }
1911
1912 /* ----------------------------------------------------------------------------
1913 The Win32 argv
1914 ------------------------------------------------------------------------- */
1915
1916 #if defined(mingw32_HOST_OS)
1917 void freeWin32ProgArgv (void);
1918
1919 void
1920 freeWin32ProgArgv (void)
1921 {
1922 int i;
1923
1924 if (win32_prog_argv != NULL) {
1925 for (i = 0; i < win32_prog_argc; i++) {
1926 stgFree(win32_prog_argv[i]);
1927 }
1928 stgFree(win32_prog_argv);
1929 }
1930
1931 win32_prog_argc = 0;
1932 win32_prog_argv = NULL;
1933 }
1934
1935 void
1936 getWin32ProgArgv(int *argc, wchar_t **argv[])
1937 {
1938 *argc = win32_prog_argc;
1939 *argv = win32_prog_argv;
1940 }
1941
1942 void
1943 setWin32ProgArgv(int argc, wchar_t *argv[])
1944 {
1945 int i;
1946
1947 freeWin32ProgArgv();
1948
1949 win32_prog_argc = argc;
1950 if (argv == NULL) {
1951 win32_prog_argv = NULL;
1952 return;
1953 }
1954
1955 win32_prog_argv = stgCallocBytes(argc + 1, sizeof (wchar_t *),
1956 "setWin32ProgArgv 1");
1957 for (i = 0; i < argc; i++) {
1958 win32_prog_argv[i] = stgMallocBytes((wcslen(argv[i]) + 1) * sizeof(wchar_t),
1959 "setWin32ProgArgv 2");
1960 wcscpy(win32_prog_argv[i], argv[i]);
1961 }
1962 win32_prog_argv[argc] = NULL;
1963 }
1964 #endif
1965
1966 /* ----------------------------------------------------------------------------
1967 The RTS argv
1968 ------------------------------------------------------------------------- */
1969
1970 static void
1971 freeRtsArgv(void)
1972 {
1973 freeArgv(rts_argc,rts_argv);
1974 rts_argc = 0;
1975 rts_argv = NULL;
1976 rts_argv_size = 0;
1977 }
1978
1979 /* ----------------------------------------------------------------------------
1980 All argvs
1981 ------------------------------------------------------------------------- */
1982
1983 void freeRtsArgs(void)
1984 {
1985 #if defined(mingw32_HOST_OS)
1986 freeWin32ProgArgv();
1987 #endif
1988 freeFullProgArgv();
1989 freeProgArgv();
1990 freeRtsArgv();
1991 }
1992
1993
1994 /*
1995 Note [OPTION_SAFE vs OPTION_UNSAFE]
1996
1997 Ticket #3910 originally pointed out that the RTS options are a potential
1998 security problem. For example the -t -s or -S flags can be used to
1999 overwrite files. This would be bad in the context of CGI scripts or
2000 setuid binaries. So we introduced a system where +RTS processing is more
2001 or less disabled unless you pass the -rtsopts flag at link time.
2002
2003 This scheme is safe enough but it also really annoyes users. They have
2004 to use -rtsopts in many circumstances: with -threaded to use -N, with
2005 -eventlog to use -l, with -prof to use any of the profiling flags. Many
2006 users just set -rtsopts globally or in project .cabal files. Apart from
2007 annoying users it reduces security because it means that deployed
2008 binaries will have all RTS options enabled rather than just profiling
2009 ones.
2010
2011 So now, we relax the set of RTS options that are available in the
2012 default -rtsopts=some case. For "deployment" ways like vanilla and
2013 -threaded we remain quite conservative. Only --info -? --help are
2014 allowed for vanilla. For -threaded, -N and -N<x> are allowed with a
2015 check that x <= num cpus.
2016
2017 For "developer" ways like -debug, -eventlog, -prof, we allow all the
2018 options that are special to that way. Some of these allow writing files,
2019 but the file written is not directly under the control of the attacker.
2020 For the setuid case (where the attacker would have control over binary
2021 name, current dir, local symlinks etc) we check if the process is
2022 running setuid/setgid and refuse all RTS option processing. Users would
2023 need to use -rtsopts=all in this case.
2024
2025 We are making the assumption that developers will not deploy binaries
2026 built in the -debug, -eventlog, -prof ways. And even if they do, the
2027 damage should be limited to DOS, information disclosure and writing
2028 files like <progname>.eventlog, not arbitrary files.
2029 */