[project @ 1999-05-11 16:47:39 by keithw]
[ghc-hetmet.git] / ghc / rts / RtsFlags.c
1 /* -----------------------------------------------------------------------------
2  * $Id: RtsFlags.c,v 1.13 1999/05/11 16:47:55 keithw Exp $
3  *
4  * (c) The AQUA Project, Glasgow University, 1994-1997
5  * (c) The GHC Team, 1998-1999
6  *
7  * Functions for parsing the argument list.
8  *
9  * ---------------------------------------------------------------------------*/
10
11 #include "Rts.h"
12 #include "RtsFlags.h"
13 #include "RtsUtils.h"
14 #include "BlockAlloc.h"
15 #include "ProfRts.h"
16
17 #if HAVE_STDLIB_H
18 #include <stdlib.h>
19 #endif
20
21 #ifdef HAVE_STRING_H
22 #include <string.h>
23 #endif
24
25 extern struct RTS_FLAGS RtsFlags;
26
27 /*
28  * Split argument lists
29  */
30 int     prog_argc; /* an "int" so as to match normal "argc" */
31 char  **prog_argv = NULL;
32 int     rts_argc;  /* ditto */
33 char   *rts_argv[MAX_RTS_ARGS];
34
35 /*
36  * constants, used later 
37  */
38 #define RTS 1
39 #define PGM 0
40
41 /* -----------------------------------------------------------------------------
42    Static function decls
43    -------------------------------------------------------------------------- */
44
45 static FILE *           /* return NULL on error */
46 open_stats_file (
47     I_ arg,
48     int argc, char *argv[],
49     int rts_argc, char *rts_argv[],
50     const char *FILENAME_FMT);
51
52 static I_ decode(const char *s);
53 static void bad_option(const char *s);
54
55 /* -----------------------------------------------------------------------------
56  * Command-line option parsing routines.
57  * ---------------------------------------------------------------------------*/
58
59 void initRtsFlagsDefaults(void)
60 {
61     RtsFlags.GcFlags.statsFile          = NULL;
62     RtsFlags.GcFlags.giveStats          = NO_GC_STATS;
63
64     RtsFlags.GcFlags.maxStkSize         = (1024 * 1024) / sizeof(W_);
65     RtsFlags.GcFlags.initialStkSize     = 1024 / sizeof(W_);
66
67     RtsFlags.GcFlags.minAllocAreaSize   = (256 * 1024)        / BLOCK_SIZE;
68     RtsFlags.GcFlags.minOldGenSize      = (1024 * 1024)       / BLOCK_SIZE;
69     RtsFlags.GcFlags.maxHeapSize        = (64  * 1024 * 1024) / BLOCK_SIZE;
70     RtsFlags.GcFlags.heapSizeSuggestion = 0;    /* none */
71     RtsFlags.GcFlags.pcFreeHeap         = 3;    /* 3% */
72     RtsFlags.GcFlags.oldGenFactor       = 2;
73     RtsFlags.GcFlags.generations        = 2;
74     RtsFlags.GcFlags.steps              = 2;
75
76     RtsFlags.GcFlags.forceGC            = rtsFalse;
77     RtsFlags.GcFlags.forcingInterval    = 5000000; /* 5MB (or words?) */
78     RtsFlags.GcFlags.ringBell           = rtsFalse;
79
80     RtsFlags.GcFlags.squeezeUpdFrames   = rtsTrue;
81
82 #if defined(PROFILING) || defined(PAR)
83     RtsFlags.CcFlags.doCostCentres      = 0;
84     RtsFlags.CcFlags.sortBy             = SORTCC_TIME;
85 #endif /* PROFILING or PAR */
86
87 #ifdef PROFILING
88     RtsFlags.ProfFlags.doHeapProfile = rtsFalse;
89
90     RtsFlags.ProfFlags.ccSelector    = NULL;
91     RtsFlags.ProfFlags.modSelector   = NULL;
92     RtsFlags.ProfFlags.grpSelector   = NULL;
93     RtsFlags.ProfFlags.descrSelector = NULL;
94     RtsFlags.ProfFlags.typeSelector  = NULL;
95     RtsFlags.ProfFlags.kindSelector  = NULL;
96 #elif defined(DEBUG)
97     RtsFlags.ProfFlags.doHeapProfile = rtsFalse;
98 #endif
99
100 /* there really shouldn't be a threads limit for concurrent mandatory threads.
101    For now, unlimitied means less than 64k (there's a storage overhead) -- SOF
102 */
103 #if defined(CONCURRENT) && !defined(GRAN)
104     RtsFlags.ConcFlags.ctxtSwitchTime   = CS_MIN_MILLISECS;  /* In milliseconds */
105     RtsFlags.ConcFlags.maxThreads       = 65536;
106     RtsFlags.ConcFlags.stkChunkSize     = 1024;
107     RtsFlags.ConcFlags.maxLocalSparks   = 65536;
108 #endif /* CONCURRENT only */
109
110 #if GRAN
111     RtsFlags.ConcFlags.ctxtSwitchTime   = CS_MIN_MILLISECS;  /* In milliseconds */
112     RtsFlags.ConcFlags.maxThreads       = 32;
113     RtsFlags.ConcFlags.stkChunkSize     = 1024;
114     RtsFlags.ConcFlags.maxLocalSparks   = 500;
115 #endif /* GRAN */
116
117 #ifdef PAR
118     RtsFlags.ParFlags.parallelStats     = rtsFalse;
119     RtsFlags.ParFlags.granSimStats      = rtsFalse;
120     RtsFlags.ParFlags.granSimStats_Binary = rtsFalse;
121
122     RtsFlags.ParFlags.outputDisabled    = rtsFalse;
123
124     RtsFlags.ParFlags.packBufferSize    = 1024;
125     RtsFlags.ParFlags.maxLocalSparks    = 4096;
126 #endif /* PAR */
127
128 #ifdef GRAN
129     RtsFlags.GranFlags.granSimStats     = rtsFalse;
130     RtsFlags.GranFlags.granSimStats_suppressed  = rtsFalse;
131     RtsFlags.GranFlags.granSimStats_Binary = rtsFalse;
132     RtsFlags.GranFlags.granSimStats_Sparks = rtsFalse;
133     RtsFlags.GranFlags.granSimStats_Heap = rtsFalse;
134     RtsFlags.GranFlags.labelling        = rtsFalse;
135     RtsFlags.GranFlags.packBufferSize   = 1024;
136     RtsFlags.GranFlags.packBufferSize_internal = GRANSIM_DEFAULT_PACK_BUFFER_SIZE;
137
138     RtsFlags.GranFlags.proc  = MAX_PROC;
139     RtsFlags.GranFlags.max_fishes = MAX_FISHES;
140     RtsFlags.GranFlags.time_slice = GRAN_TIME_SLICE;
141     RtsFlags.GranFlags.Light = rtsFalse;
142
143     RtsFlags.GranFlags.gran_latency =             LATENCY;          
144     RtsFlags.GranFlags.gran_additional_latency =  ADDITIONAL_LATENCY; 
145     RtsFlags.GranFlags.gran_fetchtime =           FETCHTIME; 
146     RtsFlags.GranFlags.gran_lunblocktime =        LOCALUNBLOCKTIME; 
147     RtsFlags.GranFlags.gran_gunblocktime =        GLOBALUNBLOCKTIME;
148     RtsFlags.GranFlags.gran_mpacktime =           MSGPACKTIME;      
149     RtsFlags.GranFlags.gran_munpacktime =         MSGUNPACKTIME;
150     RtsFlags.GranFlags.gran_mtidytime =           MSGTIDYTIME;
151
152     RtsFlags.GranFlags.gran_threadcreatetime =         THREADCREATETIME;
153     RtsFlags.GranFlags.gran_threadqueuetime =          THREADQUEUETIME;
154     RtsFlags.GranFlags.gran_threaddescheduletime =     THREADDESCHEDULETIME;
155     RtsFlags.GranFlags.gran_threadscheduletime =       THREADSCHEDULETIME;
156     RtsFlags.GranFlags.gran_threadcontextswitchtime =  THREADCONTEXTSWITCHTIME;
157
158     RtsFlags.GranFlags.gran_arith_cost =         ARITH_COST;       
159     RtsFlags.GranFlags.gran_branch_cost =        BRANCH_COST; 
160     RtsFlags.GranFlags.gran_load_cost =          LOAD_COST;        
161     RtsFlags.GranFlags.gran_store_cost =         STORE_COST; 
162     RtsFlags.GranFlags.gran_float_cost =         FLOAT_COST;       
163
164     RtsFlags.GranFlags.gran_heapalloc_cost =     HEAPALLOC_COST;
165
166     RtsFlags.GranFlags.gran_pri_spark_overhead = PRI_SPARK_OVERHEAD;        
167     RtsFlags.GranFlags.gran_pri_sched_overhead = PRI_SCHED_OVERHEAD;        
168
169     RtsFlags.GranFlags.DoFairSchedule = rtsFalse;             
170     RtsFlags.GranFlags.DoReScheduleOnFetch = rtsFalse;        
171     RtsFlags.GranFlags.DoStealThreadsFirst = rtsFalse;        
172     RtsFlags.GranFlags.SimplifiedFetch = rtsFalse;            
173     RtsFlags.GranFlags.DoAlwaysCreateThreads = rtsFalse;      
174     RtsFlags.GranFlags.DoGUMMFetching = rtsFalse;             
175     RtsFlags.GranFlags.DoThreadMigration = rtsFalse;          
176     RtsFlags.GranFlags.FetchStrategy = 2;                     
177     RtsFlags.GranFlags.PreferSparksOfLocalNodes = rtsFalse;   
178     RtsFlags.GranFlags.DoPrioritySparking = rtsFalse;         
179     RtsFlags.GranFlags.DoPriorityScheduling = rtsFalse;       
180     RtsFlags.GranFlags.SparkPriority = 0;
181     RtsFlags.GranFlags.SparkPriority2 = 0; 
182     RtsFlags.GranFlags.RandomPriorities = rtsFalse;           
183     RtsFlags.GranFlags.InversePriorities = rtsFalse;          
184     RtsFlags.GranFlags.IgnorePriorities = rtsFalse;           
185     RtsFlags.GranFlags.ThunksToPack = 0;                      
186     RtsFlags.GranFlags.RandomSteal = rtsTrue;
187     RtsFlags.GranFlags.NoForward = rtsFalse;
188     RtsFlags.GranFlags.PrintFetchMisses = rtsFalse;
189
190     RtsFlags.GranFlags.debug = 0x0;
191     RtsFlags.GranFlags.event_trace = rtsFalse;
192     RtsFlags.GranFlags.event_trace_all = rtsFalse;
193 #endif
194
195 #ifdef TICKY_TICKY
196     RtsFlags.TickyFlags.showTickyStats   = rtsFalse;
197     RtsFlags.TickyFlags.tickyFile        = NULL;
198 #endif
199 }
200
201 static const char *
202 usage_text[] = {
203 "",
204 "Usage: <prog> <args> [+RTS <rtsopts> | -RTS <args>] ... --RTS <args>",
205 "",
206 "   +RTS    Indicates run time system options follow",
207 "   -RTS    Indicates program arguments follow",
208 "  --RTS    Indicates that ALL subsequent arguments will be given to the",
209 "           program (including any of these RTS flags)",
210 "",
211 "The following run time system options are available:",
212 "",
213 "  -? -f    Prints this message and exits; the program is not executed",
214 "",
215 "  -K<size> Sets the maximum stack size (default 1M)  Egs: -K32k   -K512k",
216 "  -k<size> Sets the initial thread stack size (default 1k)  Egs: -K4k   -K2m",
217 "",
218 "  -A<size> Sets the minimum allocation area size (default 256k) Egs: -A1m -A10k",
219 "  -M<size> Sets the maximum heap size (default 64M)  Egs: -M256k -M1G",
220 "  -H<size> Sets the minimum heap size (default 0M)   Egs: -H24m  -H1G",
221 "  -m<n>%   Minimum % of heap which must be available (default 3%)",
222 "  -G<n>    Number of generations (default: 2)",
223 "  -T<n>    Number of steps in younger generations (default: 2)",
224 "  -s<file> Summary GC statistics   (default file: <program>.stat)",
225 "  -S<file> Detailed GC statistics  (with -Sstderr going to stderr)",
226 "",
227 "",
228 "  -Z       Don't squeeze out update frames on stack overflow",
229 "  -B       Sound the bell at the start of each garbage collection",
230 #if defined(PROFILING) || defined(PAR)
231 "",
232 "  -p<sort> Produce cost centre time profile  (output file <program>.prof)",
233 "             sort: T = time (default), A = alloc, C = cost centre label",
234 "  -P<sort> Produce serial time profile (output file <program>.time)",
235 "             and a -p profile with detailed tick/alloc info",
236 # if defined(PROFILING)
237 "",
238 "  -h<break-down> Heap residency profile      (output file <program>.hp)",
239 "     break-down: C = cost centre (default), M = module, G = group",
240 "                 D = closure description, Y = type description",
241 "                 T<ints>,<start> = time closure created",
242 "                    ints:  no. of interval bands plotted (default 18)",
243 "                    start: seconds after which intervals start (default 0.0)",
244 "  A subset of closures may be selected by the attached cost centre using:",
245 "    -c{mod:lab,mod:lab...}, specific module:label cost centre(s)",
246 "    -m{mod,mod...} all cost centres from the specified modules(s)",
247 "    -g{grp,grp...} all cost centres from the specified group(s)",
248 "  Selections can also be made by description, type, kind and age:",
249 "    -d{des,des...} closures with specified closure descriptions",
250 "    -y{typ,typ...} closures with specified type descriptions",
251 "    -k{knd,knd...} closures of the specified kinds",
252 "    -a<age>        closures which survived <age> complete intervals",
253 "  The selection logic used is summarised as follows:",
254 "    ([-c] or [-m] or [-g]) and ([-d] or [-y] or [-k]) and [-a]",
255 "    where an option is true if not specified",
256 # endif
257 "",
258 "  -z<tbl><size>  set hash table <size> for <tbl> (C, M, G, D or Y)",
259 "",
260 "  -i<secs> Number of seconds in a profiling interval (default 1.0):",
261 "           heap profile (-h) and/or serial time profile (-P) frequency",
262 #endif /* PROFILING or PAR */
263 #if !defined(PROFILING) && defined(DEBUG)
264 "",
265 "  -h<break-down> Debugging Heap residency profile",
266 "                 (output file <program>.hp)",
267 "     break-down: L = closure label (default)",
268 "                 T = closure type (constructor, thunk etc.)",
269 #endif
270 "",
271 #if defined(TICKY_TICKY)
272 "  -r<file>  Produce reduction profiling statistics (with -rstderr for stderr)",
273 "",
274 #endif
275 # ifdef PAR
276 "  -N<n>     Use <n> PVMish processors in parallel (default: 2)",
277 /* NB: the -N<n> is implemented by the driver!! */
278 # endif
279 "  -C<secs>  Context-switch interval in seconds",
280 "                (0 or no argument means switch as often as possible)",
281 "                the default is .01 sec; resolution is .01 sec",
282 "  -e<size>        Size of spark pools (default 100)",
283 # ifdef PAR
284 "  -q        Enable activity profile (output files in ~/<program>*.gr)",
285 "  -qb       Enable binary activity profile (output file /tmp/<program>.gb)",
286 "  -Q<size>  Set pack-buffer size (default: 1024)",
287 # else
288 "  -q[v]     Enable quasi-parallel profile (output file <program>.qp)",
289 # endif
290 "  -t<num>   Set maximum number of advisory threads per PE (default 32)",
291 "  -o<num>   Set stack chunk size (default 1024)",
292 # ifdef PAR
293 "  -d        Turn on PVM-ish debugging",
294 "  -O        Disable output for performance measurement",
295 # endif /* PAR */
296 # ifdef GRAN  /* ToDo: fill in decent Docu here */
297 "  -b...     All GranSim options start with -b; see GranSim User's Guide for details",
298 # endif
299 "",
300 "Other RTS options may be available for programs compiled a different way.",
301 "The GHC User's Guide has full details.",
302 "",
303 0
304 };
305
306 static __inline__ rtsBool
307 strequal(const char *a, const char * b)
308 {
309     return(strcmp(a, b) == 0);
310 }
311
312 void
313 setupRtsFlags(int *argc, char *argv[], int *rts_argc, char *rts_argv[])
314 {
315     rtsBool error = rtsFalse;
316     I_ mode;
317     I_ arg, total_arg;
318     char *last_slash;
319
320     /* Remove directory from argv[0] -- default files in current directory */
321
322     if ((last_slash = (char *) strrchr(argv[0], '/')) != NULL)
323         strcpy(argv[0], last_slash+1);
324
325     /* Split arguments (argv) into PGM (argv) and RTS (rts_argv) parts */
326     /*   argv[0] must be PGM argument -- leave in argv                 */
327
328     total_arg = *argc;
329     arg = 1;
330
331     *argc = 1;
332     *rts_argc = 0;
333
334     for (mode = PGM; arg < total_arg && ! strequal("--RTS", argv[arg]); arg++) {
335         if (strequal("+RTS", argv[arg])) {
336             mode = RTS;
337         }
338         else if (strequal("-RTS", argv[arg])) {
339             mode = PGM;
340         }
341         else if (mode == RTS && *rts_argc < MAX_RTS_ARGS-1) {
342             rts_argv[(*rts_argc)++] = argv[arg];
343         }
344         else if (mode == PGM) {
345             argv[(*argc)++] = argv[arg];
346         }
347         else {
348           barf("too many RTS arguments (max %d)", MAX_RTS_ARGS-1);
349         }
350     }
351     if (arg < total_arg) {
352         /* arg must be --RTS; process remaining program arguments */
353         while (++arg < total_arg) {
354             argv[(*argc)++] = argv[arg];
355         }
356     }
357     argv[*argc] = (char *) 0;
358     rts_argv[*rts_argc] = (char *) 0;
359
360     /* Process RTS (rts_argv) part: mainly to determine statsfile */
361
362     for (arg = 0; arg < *rts_argc; arg++) {
363         if (rts_argv[arg][0] != '-') {
364             fflush(stdout);
365             fprintf(stderr, "setupRtsFlags: Unexpected RTS argument: %s\n",
366                     rts_argv[arg]);
367             error = rtsTrue;
368
369         } else {
370             switch(rts_argv[arg][1]) {
371
372               /* process: general args, then PROFILING-only ones,
373                  then CONCURRENT-only, PARallel-only, GRAN-only,
374                  TICKY-only (same order as defined in RtsFlags.lh);
375                  within those groups, mostly in case-insensitive
376                  alphabetical order.
377               */
378
379 #ifdef TICKY_TICKY
380 # define TICKY_BUILD_ONLY(x) x
381 #else
382 # define TICKY_BUILD_ONLY(x) \
383 fprintf(stderr, "setupRtsFlags: GHC not built for: ticky-ticky stats\n"); \
384 error = rtsTrue;
385 #endif
386
387 #if defined(PROFILING) 
388 # define COST_CENTRE_USING_BUILD_ONLY(x) x
389 #else
390 # define COST_CENTRE_USING_BUILD_ONLY(x) \
391 fprintf(stderr, "setupRtsFlags: GHC not built for: -prof or -parallel\n"); \
392 error = rtsTrue;
393 #endif
394
395 #ifdef PROFILING
396 # define PROFILING_BUILD_ONLY(x)   x
397 #else
398 # define PROFILING_BUILD_ONLY(x) \
399 fprintf(stderr, "setupRtsFlags: GHC not built for: -prof\n"); \
400 error = rtsTrue;
401 #endif
402
403 #ifdef PAR
404 # define PAR_BUILD_ONLY(x)      x
405 #else
406 # define PAR_BUILD_ONLY(x) \
407 fprintf(stderr, "setupRtsFlags: GHC not built for: -parallel\n"); \
408 error = rtsTrue;
409 #endif
410
411 #ifdef GRAN
412 # define GRAN_BUILD_ONLY(x)     x
413 #else
414 # define GRAN_BUILD_ONLY(x) \
415 fprintf(stderr, "setupRtsFlags: GHC not built for: -gransim\n"); \
416 error = rtsTrue;
417 #endif
418
419               /* =========== GENERAL ========================== */
420               case '?':
421               case 'f':
422                 error = rtsTrue;
423                 break;
424
425               case 'A':
426                 RtsFlags.GcFlags.minAllocAreaSize
427                   = decode(rts_argv[arg]+2) / BLOCK_SIZE;
428                 if (RtsFlags.GcFlags.minAllocAreaSize <= 0) {
429                   bad_option(rts_argv[arg]);
430                 }
431                 break;
432
433               case 'B':
434                 RtsFlags.GcFlags.ringBell = rtsTrue;
435                 break;
436
437               case 'F':
438                 RtsFlags.GcFlags.oldGenFactor = atof(rts_argv[arg]+2);
439               
440                 if (RtsFlags.GcFlags.oldGenFactor < 0)
441                   bad_option( rts_argv[arg] );
442                 break;
443               
444 #ifdef DEBUG
445               case 'D':
446                 /* hack warning: interpret the flags as a binary number */
447                 { 
448                    I_ n = decode(rts_argv[arg]+2);
449                    if (n     &1) RtsFlags.DebugFlags.scheduler   = rtsTrue;
450                    if ((n>>1)&1) RtsFlags.DebugFlags.evaluator   = rtsTrue;
451                    if ((n>>2)&1) RtsFlags.DebugFlags.codegen     = rtsTrue;
452                    if ((n>>3)&1) RtsFlags.DebugFlags.weak        = rtsTrue;
453                    if ((n>>4)&1) RtsFlags.DebugFlags.gccafs      = rtsTrue;
454                    if ((n>>5)&1) RtsFlags.DebugFlags.gc          = rtsTrue;
455                    if ((n>>6)&1) RtsFlags.DebugFlags.block_alloc = rtsTrue;
456                    if ((n>>7)&1) RtsFlags.DebugFlags.sanity      = rtsTrue;
457                    if ((n>>8)&1) RtsFlags.DebugFlags.stable      = rtsTrue;
458                    if ((n>>9)&1) RtsFlags.DebugFlags.prof        = rtsTrue;
459                 }
460                 break;
461 #endif
462
463               case 'K':
464                 RtsFlags.GcFlags.maxStkSize = 
465                   decode(rts_argv[arg]+2) / sizeof(W_);
466
467                 if (RtsFlags.GcFlags.maxStkSize == 0) 
468                   bad_option( rts_argv[arg] );
469                 break;
470
471               case 'k':
472                 RtsFlags.GcFlags.initialStkSize = 
473                   decode(rts_argv[arg]+2) / sizeof(W_);
474
475                 if (RtsFlags.GcFlags.initialStkSize == 0) 
476                   bad_option( rts_argv[arg] );
477                 break;
478
479               case 'M':
480                 RtsFlags.GcFlags.maxHeapSize = 
481                   decode(rts_argv[arg]+2) / BLOCK_SIZE;
482                 /* user give size in *bytes* but "maxHeapSize" is in *blocks* */
483
484                 if (RtsFlags.GcFlags.maxHeapSize <= 0) {
485                   bad_option(rts_argv[arg]);
486                 }
487                 break;
488
489               case 'm':
490                 RtsFlags.GcFlags.pcFreeHeap = atof(rts_argv[arg]+2);
491
492                 if (RtsFlags.GcFlags.pcFreeHeap < 0 || 
493                     RtsFlags.GcFlags.pcFreeHeap > 100)
494                   bad_option( rts_argv[arg] );
495                 break;
496
497               case 'G':
498                 RtsFlags.GcFlags.generations = decode(rts_argv[arg]+2);
499                 if (RtsFlags.GcFlags.generations < 1) {
500                   bad_option(rts_argv[arg]);
501                 }
502                 break;
503
504               case 'T':
505                 RtsFlags.GcFlags.steps = decode(rts_argv[arg]+2);
506                 if (RtsFlags.GcFlags.steps < 1) {
507                   bad_option(rts_argv[arg]);
508                 }
509                 break;
510
511               case 'H':
512                 RtsFlags.GcFlags.heapSizeSuggestion = 
513                   decode(rts_argv[arg]+2) / BLOCK_SIZE;
514
515                 if (RtsFlags.GcFlags.heapSizeSuggestion <= 0) {
516                   bad_option(rts_argv[arg]);
517                 }
518                 break;
519
520               case 'j': /* force GC option */
521                 RtsFlags.GcFlags.forceGC = rtsTrue;
522                 if (rts_argv[arg][2]) {
523                     RtsFlags.GcFlags.forcingInterval
524                         = decode(rts_argv[arg]+2) / sizeof(W_);
525                 }
526                 break;
527
528               case 'S': /* NB: no difference at present ! */
529               case 's':
530                 RtsFlags.GcFlags.giveStats ++; /* will be VERBOSE_GC_STATS */
531 #ifdef PAR
532                 /* Opening all those files would almost certainly fail... */
533                 RtsFlags.ParFlags.parallelStats = rtsTrue;
534                 RtsFlags.GcFlags.statsFile = stderr; /* temporary; ToDo: rm */
535 #else
536                 RtsFlags.GcFlags.statsFile
537                   = open_stats_file(arg, *argc, argv,
538                         *rts_argc, rts_argv, STAT_FILENAME_FMT);
539
540                 if (RtsFlags.GcFlags.statsFile == NULL) error = rtsTrue;
541 #endif
542                 break;
543
544               case 'Z':
545                 RtsFlags.GcFlags.squeezeUpdFrames = rtsFalse;
546                 break;
547
548               /* =========== PROFILING ========================== */
549
550               case 'P': /* detailed cost centre profiling (time/alloc) */
551                 COST_CENTRE_USING_BUILD_ONLY(
552                 RtsFlags.CcFlags.doCostCentres++;
553                 )
554               case 'p': /* cost centre profiling (time/alloc) */
555                 COST_CENTRE_USING_BUILD_ONLY(
556                 RtsFlags.CcFlags.doCostCentres++;
557
558                 switch (rts_argv[arg][2]) {
559                   case SORTCC_LABEL:
560                   case SORTCC_TIME:
561                   case SORTCC_ALLOC:
562                         RtsFlags.CcFlags.sortBy = rts_argv[arg][2];
563                     break;
564                   default:
565                         RtsFlags.CcFlags.sortBy = SORTCC_TIME;
566                     break;
567                 }
568                 ) break;
569
570               case 'i': /* serial profiling -- initial timer interval */
571                 COST_CENTRE_USING_BUILD_ONLY(
572                 interval_ticks = (I_) ((atof(rts_argv[arg]+2) * TICK_FREQUENCY));
573                 if (interval_ticks <= 0)
574                     interval_ticks = 1;
575                 ) break;
576
577               case 'h': /* serial heap profile */
578 #if !defined(PROFILING) && defined(DEBUG)
579                 switch (rts_argv[arg][2]) {
580                   case '\0':
581                   case 'L':
582                     RtsFlags.ProfFlags.doHeapProfile = HEAP_BY_INFOPTR;
583                     break;
584                   case 'T':
585                     RtsFlags.ProfFlags.doHeapProfile = HEAP_BY_CLOSURE_TYPE;
586                     break;
587                   default:
588                     fprintf(stderr, "Invalid heap profile option: %s\n",
589                             rts_argv[arg]);
590                     error = rtsTrue;
591                 }
592 #else
593                 PROFILING_BUILD_ONLY(
594                 switch (rts_argv[arg][2]) {
595                   case '\0':
596                   case CCchar:
597                     RtsFlags.ProfFlags.doHeapProfile = HEAP_BY_CC;
598                     break;
599                   case MODchar:
600                     RtsFlags.ProfFlags.doHeapProfile = HEAP_BY_MOD;
601                     break;
602                   case GRPchar:
603                     RtsFlags.ProfFlags.doHeapProfile = HEAP_BY_GRP;
604                     break;
605                   case DESCRchar:
606                     RtsFlags.ProfFlags.doHeapProfile = HEAP_BY_DESCR;
607                     break;
608                   case TYPEchar:
609                     RtsFlags.ProfFlags.doHeapProfile = HEAP_BY_TYPE;
610                     break;
611                   case TIMEchar:
612                     RtsFlags.ProfFlags.doHeapProfile = HEAP_BY_TIME;
613                     if (rts_argv[arg][3]) {
614                         char *start_str = strchr(rts_argv[arg]+3, ',');
615                         I_ intervals;
616                         if (start_str) *start_str = '\0';
617
618                         if ((intervals = decode(rts_argv[arg]+3)) != 0) {
619                             time_intervals = (hash_t) intervals;
620                             /* ToDo: and what if it *is* zero intervals??? */
621                         }
622                         if (start_str) {
623                             earlier_ticks = (I_)((atof(start_str + 1) * TICK_FREQUENCY));
624                         }
625                     }
626                     break;
627                   default:
628                     fprintf(stderr, "Invalid heap profile option: %s\n",
629                             rts_argv[arg]);
630                     error = rtsTrue;
631                 }
632                 ) 
633 #endif
634                 break;
635
636               case 'z': /* size of index tables */
637                 PROFILING_BUILD_ONLY(
638                 switch (rts_argv[arg][2]) {
639                   case CCchar:
640                     max_cc_no = (hash_t) decode(rts_argv[arg]+3);
641                     if (max_cc_no == 0) {
642                         fprintf(stderr, "Bad number of cost centres %s\n", rts_argv[arg]);
643                         error = rtsTrue;
644                     }
645                     break;
646                   case MODchar:
647                     max_mod_no = (hash_t) decode(rts_argv[arg]+3);
648                     if (max_mod_no == 0) {
649                         fprintf(stderr, "Bad number of modules %s\n", rts_argv[arg]);
650                         error = rtsTrue;
651                     }
652                     break;
653                   case GRPchar:
654                     max_grp_no = (hash_t) decode(rts_argv[arg]+3);
655                     if (max_grp_no == 0) {
656                         fprintf(stderr, "Bad number of groups %s\n", rts_argv[arg]);
657                         error = rtsTrue;
658                     }
659                     break;
660                   case DESCRchar:
661                     max_descr_no = (hash_t) decode(rts_argv[arg]+3);
662                     if (max_descr_no == 0) {
663                         fprintf(stderr, "Bad number of closure descriptions %s\n", rts_argv[arg]);
664                         error = rtsTrue;
665                     }
666                     break;
667                   case TYPEchar:
668                     max_type_no = (hash_t) decode(rts_argv[arg]+3);
669                     if (max_type_no == 0) {
670                         fprintf(stderr, "Bad number of type descriptions %s\n", rts_argv[arg]);
671                         error = rtsTrue;
672                     }
673                     break;
674                   default:
675                     fprintf(stderr, "Invalid index table size option: %s\n",
676                             rts_argv[arg]);
677                     error = rtsTrue;
678                 }
679                 ) break;
680
681               case 'c': /* cost centre label select */
682               case 'g': /* cost centre group select */
683               case 'd': /* closure descr select */
684               case 'y': /* closure type select */
685                 PROFILING_BUILD_ONLY(
686                 {char *left  = strchr(rts_argv[arg], '{');
687                  char *right = strrchr(rts_argv[arg], '}');
688
689                 if (! left || ! right ||
690                         strrchr(rts_argv[arg], '{') != left ||
691                          strchr(rts_argv[arg], '}') != right) {
692                     fprintf(stderr, "Invalid heap profiling selection bracketing\n   %s\n", rts_argv[arg]);
693                     error = rtsTrue;
694                 } else {
695                     *right = '\0';
696                     switch (rts_argv[arg][1]) {
697                       case 'c': /* cost centre label select */
698                         RtsFlags.ProfFlags.ccSelector = left + 1;
699                         break;
700                       case 'm': /* cost centre module select */
701                         RtsFlags.ProfFlags.modSelector = left + 1;
702                         break;
703                       case 'g': /* cost centre group select */
704                         RtsFlags.ProfFlags.grpSelector = left + 1;
705                         break;
706                       case 'd': /* closure descr select */
707                         RtsFlags.ProfFlags.descrSelector = left + 1;
708                         break;
709                       case 'y': /* closure type select */
710                         RtsFlags.ProfFlags.typeSelector = left + 1;
711                         break;
712                       case 'k': /* closure kind select */
713                         RtsFlags.ProfFlags.kindSelector = left + 1;
714                         break;
715                     }
716                 }}
717                 ) break;
718
719               /* =========== CONCURRENT ========================= */
720               case 'C': /* context switch interval */
721                 if (rts_argv[arg][2] == '\0')
722                     RtsFlags.ConcFlags.ctxtSwitchTime = 0;
723                 else {
724                     I_ cst; /* tmp */
725
726                     /* Convert to milliseconds */
727                     cst = (I_) ((atof(rts_argv[arg]+2) * 1000));
728                     cst = (cst / CS_MIN_MILLISECS) * CS_MIN_MILLISECS;
729                     if (cst < CS_MIN_MILLISECS)
730                         cst = CS_MIN_MILLISECS;
731
732                     RtsFlags.ConcFlags.ctxtSwitchTime = cst;
733                 }
734                 break;
735
736               case 't':
737                 if (rts_argv[arg][2] != '\0') {
738                     RtsFlags.ConcFlags.maxThreads
739                       = strtol(rts_argv[arg]+2, (char **) NULL, 10);
740                 } else {
741                     fprintf(stderr, "setupRtsFlags: missing size for -t\n");
742                     error = rtsTrue;
743                 }
744                 break;
745
746               /* =========== PARALLEL =========================== */
747               case 'e':
748                 PAR_BUILD_ONLY(
749                 if (rts_argv[arg][2] != '\0') { /* otherwise, stick w/ the default */
750
751                     RtsFlags.ParFlags.maxLocalSparks
752                       = strtol(rts_argv[arg]+2, (char **) NULL, 10);
753
754                     if (RtsFlags.ParFlags.maxLocalSparks <= 0) {
755                         fprintf(stderr, "setupRtsFlags: bad value for -e\n");
756                         error = rtsTrue;
757                     }
758                 }
759                 ) break;
760
761               case 'O':
762                 PAR_BUILD_ONLY(
763                 RtsFlags.ParFlags.outputDisabled = rtsTrue;
764                 ) break;
765
766               case 'q': /* activity profile option */
767                 PAR_BUILD_ONLY(
768                 if (rts_argv[arg][2] == 'b')
769                     RtsFlags.ParFlags.granSimStats_Binary = rtsTrue;
770                 else
771                     RtsFlags.ParFlags.granSimStats = rtsTrue;
772                 ) break;
773
774 #if 0 /* or??? */
775               case 'q': /* quasi-parallel profile option */
776                 GRAN_BUILD_ONLY (
777                 if (rts_argv[arg][2] == 'v')
778                     do_qp_prof = 2;
779                 else
780                     do_qp_prof++;
781                 ) break;
782 #endif /* 0??? */
783
784               case 'Q': /* Set pack buffer size */
785                 PAR_BUILD_ONLY(
786                 if (rts_argv[arg][2] != '\0') {
787                     RtsFlags.ParFlags.packBufferSize = decode(rts_argv[arg]+2);
788                 } else {
789                     fprintf(stderr, "setupRtsFlags: missing size of PackBuffer (for -Q)\n");
790                     error = rtsTrue;
791                 }
792                 ) break;
793
794               /* =========== GRAN =============================== */
795
796               case 'b':
797                 GRAN_BUILD_ONLY(
798                 process_gran_option(arg, rts_argc, rts_argv, &error);
799                 ) break;
800
801               /* =========== TICKY ============================== */
802
803               case 'r': /* Basic profiling stats */
804                 TICKY_BUILD_ONLY(
805
806                 RtsFlags.TickyFlags.showTickyStats = rtsTrue;
807                 RtsFlags.TickyFlags.tickyFile
808                   = open_stats_file(arg, *argc, argv,
809                         *rts_argc, rts_argv, TICKY_FILENAME_FMT);
810
811                 if (RtsFlags.TickyFlags.tickyFile == NULL) error = rtsTrue;
812                 ) break;
813
814               /* =========== OH DEAR ============================ */
815               default:
816                 fprintf(stderr, "setupRtsFlags: Unknown RTS option: %s\n",rts_argv[arg]);
817                 error = rtsTrue;
818                 break;
819             }
820         }
821     }
822     if (error) {
823         const char **p;
824
825         fflush(stdout);
826         for (p = usage_text; *p; p++)
827             fprintf(stderr, "%s\n", *p);
828         stg_exit(EXIT_FAILURE);
829     }
830
831 }
832
833 static FILE *           /* return NULL on error */
834 open_stats_file (
835     I_ arg,
836     int argc, char *argv[],
837     int rts_argc, char *rts_argv[],
838     const char *FILENAME_FMT)
839 {
840     FILE *f = NULL;
841
842     if (strequal(rts_argv[arg]+2, "stderr")) /* use real stderr */
843         f = stderr;
844     else if (rts_argv[arg][2] != '\0')      /* stats file specified */
845         f = fopen(rts_argv[arg]+2,"w");
846     else {
847         char stats_filename[STATS_FILENAME_MAXLEN]; /* default <program>.<ext> */
848         sprintf(stats_filename, FILENAME_FMT, argv[0]);
849         f = fopen(stats_filename,"w");
850     }
851     if (f == NULL) {
852         fprintf(stderr, "Can't open stats file %s\n", rts_argv[arg]+2);
853     } else {
854         /* Write argv and rtsv into start of stats file */
855         I_ count;
856         for(count = 0; count < argc; count++)
857             fprintf(f, "%s ", argv[count]);
858         fprintf(f, "+RTS ");
859         for(count = 0; count < rts_argc; count++)
860             fprintf(f, "%s ", rts_argv[count]);
861         fprintf(f, "\n");
862     }
863
864     return(f);
865 }
866
867 static I_
868 decode(const char *s)
869 {
870     I_ c;
871     StgDouble m;
872
873     if (!*s)
874         return 0;
875
876     m = atof(s);
877     c = s[strlen(s)-1];
878
879     if (c == 'g' || c == 'G')
880         m *= 1000*1000*1000;    /* UNchecked! */
881     else if (c == 'm' || c == 'M')
882         m *= 1000*1000;                 /* We do not use powers of 2 (1024) */
883     else if (c == 'k' || c == 'K')      /* to avoid possible bad effects on */
884         m *= 1000;                      /* a direct-mapped cache.           */ 
885     else if (c == 'w' || c == 'W')
886         m *= sizeof(W_);
887
888     return (I_)m;
889 }
890
891 static void
892 bad_option(const char *s)
893 {
894   fflush(stdout);
895   fprintf(stderr, "initSM: Bad RTS option: %s\n", s);
896   stg_exit(EXIT_FAILURE);
897 }