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