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