[project @ 1999-01-19 15:07:53 by simonm]
[ghc-hetmet.git] / ghc / rts / RtsFlags.c
1 /* -----------------------------------------------------------------------------
2  * $Id: RtsFlags.c,v 1.4 1999/01/19 15:07:55 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.pcFreeHeap         = 3;    /* 3% */
68     RtsFlags.GcFlags.oldGenFactor       = 2;
69     RtsFlags.GcFlags.generations        = 2;
70
71     RtsFlags.GcFlags.forceGC            = rtsFalse;
72     RtsFlags.GcFlags.forcingInterval    = 5000000; /* 5MB (or words?) */
73     RtsFlags.GcFlags.ringBell           = rtsFalse;
74
75     RtsFlags.GcFlags.squeezeUpdFrames   = rtsTrue;
76
77 #if defined(PROFILING) || defined(PAR)
78     RtsFlags.CcFlags.doCostCentres      = 0;
79     RtsFlags.CcFlags.sortBy             = SORTCC_TIME;
80 #endif /* PROFILING or PAR */
81
82 #ifdef PROFILING
83     RtsFlags.ProfFlags.doHeapProfile = rtsFalse;
84
85     RtsFlags.ProfFlags.ccSelector    = NULL;
86     RtsFlags.ProfFlags.modSelector   = NULL;
87     RtsFlags.ProfFlags.grpSelector   = NULL;
88     RtsFlags.ProfFlags.descrSelector = NULL;
89     RtsFlags.ProfFlags.typeSelector  = NULL;
90     RtsFlags.ProfFlags.kindSelector  = NULL;
91 #elif defined(DEBUG)
92     RtsFlags.ProfFlags.doHeapProfile = rtsFalse;
93 #endif
94
95 /* there really shouldn't be a threads limit for concurrent mandatory threads.
96    For now, unlimitied means less than 64k (there's a storage overhead) -- SOF
97 */
98 #if defined(CONCURRENT) && !defined(GRAN)
99     RtsFlags.ConcFlags.ctxtSwitchTime   = CS_MIN_MILLISECS;  /* In milliseconds */
100     RtsFlags.ConcFlags.maxThreads       = 65536;
101     RtsFlags.ConcFlags.stkChunkSize     = 1024;
102     RtsFlags.ConcFlags.maxLocalSparks   = 65536;
103 #endif /* CONCURRENT only */
104
105 #if GRAN
106     RtsFlags.ConcFlags.ctxtSwitchTime   = CS_MIN_MILLISECS;  /* In milliseconds */
107     RtsFlags.ConcFlags.maxThreads       = 32;
108     RtsFlags.ConcFlags.stkChunkSize     = 1024;
109     RtsFlags.ConcFlags.maxLocalSparks   = 500;
110 #endif /* GRAN */
111
112 #ifdef PAR
113     RtsFlags.ParFlags.parallelStats     = rtsFalse;
114     RtsFlags.ParFlags.granSimStats      = rtsFalse;
115     RtsFlags.ParFlags.granSimStats_Binary = rtsFalse;
116
117     RtsFlags.ParFlags.outputDisabled    = rtsFalse;
118
119     RtsFlags.ParFlags.packBufferSize    = 1024;
120     RtsFlags.ParFlags.maxLocalSparks    = 4096;
121 #endif /* PAR */
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
194     AllFlags.doUpdEntryCounts           = rtsTrue; /*ToDo:move? */
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 "  -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 (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 # endif
252 "",
253 "  -z<tbl><size>  set hash table <size> for <tbl> (C, M, G, D or Y)",
254 "",
255 "  -i<secs> Number of seconds in a profiling interval (default 1.0):",
256 "           heap profile (-h) and/or serial time profile (-P) frequency",
257 #endif /* PROFILING or PAR */
258 #if !defined(PROFILING) && defined(DEBUG)
259 "",
260 "  -h<break-down> Debugging Heap residency profile",
261 "                 (output file <program>.hp)",
262 "     break-down: L = closure label (default)",
263 "                 T = closure type (constructor, thunk etc.)",
264 #endif
265 "",
266 #if defined(TICKY_TICKY)
267 "  -r<file>  Produce reduction profiling statistics (with -rstderr for stderr)",
268 "",
269 #endif
270 "  -T<level> Trace garbage collection execution (debugging)",
271 "",
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 "  -e<size>        Size of spark pools (default 100)",
280 # ifdef PAR
281 "  -q        Enable activity profile (output files in ~/<program>*.gr)",
282 "  -qb       Enable binary activity profile (output file /tmp/<program>.gb)",
283 "  -Q<size>  Set pack-buffer size (default: 1024)",
284 # else
285 "  -q[v]     Enable quasi-parallel profile (output file <program>.qp)",
286 # endif
287 "  -t<num>   Set maximum number of advisory threads per PE (default 32)",
288 "  -o<num>   Set stack chunk size (default 1024)",
289 # ifdef PAR
290 "  -d        Turn on PVM-ish debugging",
291 "  -O        Disable output for performance measurement",
292 # endif /* PAR */
293 # ifdef GRAN  /* ToDo: fill in decent Docu here */
294 "  -b...     All GranSim options start with -b; see GranSim User's Guide for details",
295 # endif
296 "",
297 "Other RTS options may be available for programs compiled a different way.",
298 "The GHC User's Guide has full details.",
299 "",
300 0
301 };
302
303 static __inline__ rtsBool
304 strequal(const char *a, const char * b)
305 {
306     return(strcmp(a, b) == 0);
307 }
308
309 void
310 setupRtsFlags(int *argc, char *argv[], int *rts_argc, char *rts_argv[])
311 {
312     rtsBool error = rtsFalse;
313     I_ mode;
314     I_ arg, total_arg;
315     char *last_slash;
316
317     /* Remove directory from argv[0] -- default files in current directory */
318
319     if ((last_slash = (char *) strrchr(argv[0], '/')) != NULL)
320         strcpy(argv[0], last_slash+1);
321
322     /* Split arguments (argv) into PGM (argv) and RTS (rts_argv) parts */
323     /*   argv[0] must be PGM argument -- leave in argv                 */
324
325     total_arg = *argc;
326     arg = 1;
327
328     *argc = 1;
329     *rts_argc = 0;
330
331     for (mode = PGM; arg < total_arg && ! strequal("--RTS", argv[arg]); arg++) {
332         if (strequal("+RTS", argv[arg])) {
333             mode = RTS;
334         }
335         else if (strequal("-RTS", argv[arg])) {
336             mode = PGM;
337         }
338         else if (mode == RTS && *rts_argc < MAX_RTS_ARGS-1) {
339             rts_argv[(*rts_argc)++] = argv[arg];
340         }
341         else if (mode == PGM) {
342             argv[(*argc)++] = argv[arg];
343         }
344         else {
345           barf("too many RTS arguments (max %d)", MAX_RTS_ARGS-1);
346         }
347     }
348     if (arg < total_arg) {
349         /* arg must be --RTS; process remaining program arguments */
350         while (++arg < total_arg) {
351             argv[(*argc)++] = argv[arg];
352         }
353     }
354     argv[*argc] = (char *) 0;
355     rts_argv[*rts_argc] = (char *) 0;
356
357     /* Process RTS (rts_argv) part: mainly to determine statsfile */
358
359     for (arg = 0; arg < *rts_argc; arg++) {
360         if (rts_argv[arg][0] != '-') {
361             fflush(stdout);
362             fprintf(stderr, "setupRtsFlags: Unexpected RTS argument: %s\n",
363                     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               */
375
376 #ifdef TICKY_TICKY
377 # define TICKY_BUILD_ONLY(x) x
378 #else
379 # define TICKY_BUILD_ONLY(x) \
380 fprintf(stderr, "setupRtsFlags: GHC not built for: ticky-ticky stats\n"); \
381 error = rtsTrue;
382 #endif
383
384 #if defined(PROFILING) 
385 # define COST_CENTRE_USING_BUILD_ONLY(x) x
386 #else
387 # define COST_CENTRE_USING_BUILD_ONLY(x) \
388 fprintf(stderr, "setupRtsFlags: GHC not built for: -prof or -parallel\n"); \
389 error = rtsTrue;
390 #endif
391
392 #ifdef PROFILING
393 # define PROFILING_BUILD_ONLY(x)   x
394 #else
395 # define PROFILING_BUILD_ONLY(x) \
396 fprintf(stderr, "setupRtsFlags: GHC not built for: -prof\n"); \
397 error = rtsTrue;
398 #endif
399
400 #ifdef PAR
401 # define PAR_BUILD_ONLY(x)      x
402 #else
403 # define PAR_BUILD_ONLY(x) \
404 fprintf(stderr, "setupRtsFlags: GHC not built for: -parallel\n"); \
405 error = rtsTrue;
406 #endif
407
408 #ifdef GRAN
409 # define GRAN_BUILD_ONLY(x)     x
410 #else
411 # define GRAN_BUILD_ONLY(x) \
412 fprintf(stderr, "setupRtsFlags: GHC not built for: -gransim\n"); \
413 error = rtsTrue;
414 #endif
415
416               /* =========== GENERAL ========================== */
417               case '?':
418               case 'f':
419                 error = rtsTrue;
420                 break;
421
422               case 'A':
423                 RtsFlags.GcFlags.minAllocAreaSize
424                   = decode(rts_argv[arg]+2) / BLOCK_SIZE;
425                 if (RtsFlags.GcFlags.minAllocAreaSize <= 0) {
426                   bad_option(rts_argv[arg]);
427                 }
428                 break;
429
430               case 'B':
431                 RtsFlags.GcFlags.ringBell = rtsTrue;
432                 break;
433
434               case 'F':
435                 RtsFlags.GcFlags.oldGenFactor = atof(rts_argv[arg]+2);
436               
437                 if (RtsFlags.GcFlags.oldGenFactor < 0)
438                   bad_option( rts_argv[arg] );
439                 break;
440               
441 #ifdef DEBUG
442               case 'D':
443                 /* hack warning: interpret the flags as a binary number */
444                 *(int*)(&RtsFlags.DebugFlags) = decode(rts_argv[arg]+2);
445                 break;
446 #endif
447
448               case 'K':
449                 RtsFlags.GcFlags.maxStkSize = 
450                   decode(rts_argv[arg]+2) / sizeof(W_);
451
452                 if (RtsFlags.GcFlags.maxStkSize == 0) 
453                   bad_option( rts_argv[arg] );
454                 break;
455
456               case 'k':
457                 RtsFlags.GcFlags.initialStkSize = 
458                   decode(rts_argv[arg]+2) / sizeof(W_);
459
460                 if (RtsFlags.GcFlags.initialStkSize == 0) 
461                   bad_option( rts_argv[arg] );
462                 break;
463
464               case 'M':
465                 RtsFlags.GcFlags.maxHeapSize = 
466                   decode(rts_argv[arg]+2) / BLOCK_SIZE;
467                 /* user give size in *bytes* but "maxHeapSize" is in *blocks* */
468
469                 if (RtsFlags.GcFlags.maxHeapSize <= 0) {
470                   bad_option(rts_argv[arg]);
471                 }
472                 break;
473
474               case 'm':
475                 RtsFlags.GcFlags.pcFreeHeap = atof(rts_argv[arg]+2);
476
477                 if (RtsFlags.GcFlags.pcFreeHeap < 0 || 
478                     RtsFlags.GcFlags.pcFreeHeap > 100)
479                   bad_option( rts_argv[arg] );
480                 break;
481
482               case 'G':
483                 RtsFlags.GcFlags.generations = decode(rts_argv[arg]+2);
484                 if (RtsFlags.GcFlags.generations <= 1) {
485                   bad_option(rts_argv[arg]);
486                 }
487                 break;
488
489               case 'H':
490                 /* ignore for compatibility with older versions */
491                 break;
492
493               case 'j': /* force GC option */
494                 RtsFlags.GcFlags.forceGC = rtsTrue;
495                 if (rts_argv[arg][2]) {
496                     RtsFlags.GcFlags.forcingInterval
497                         = decode(rts_argv[arg]+2) / sizeof(W_);
498                 }
499                 break;
500
501               case 'S': /* NB: no difference at present ! */
502               case 's':
503                 RtsFlags.GcFlags.giveStats ++; /* will be VERBOSE_GC_STATS */
504 #ifdef PAR
505                 /* Opening all those files would almost certainly fail... */
506                 RtsFlags.ParFlags.parallelStats = rtsTrue;
507                 RtsFlags.GcFlags.statsFile = stderr; /* temporary; ToDo: rm */
508 #else
509                 RtsFlags.GcFlags.statsFile
510                   = open_stats_file(arg, *argc, argv,
511                         *rts_argc, rts_argv, STAT_FILENAME_FMT);
512
513                 if (RtsFlags.GcFlags.statsFile == NULL) error = rtsTrue;
514 #endif
515                 break;
516
517               case 'Z':
518                 RtsFlags.GcFlags.squeezeUpdFrames = rtsFalse;
519                 break;
520
521               /* =========== PROFILING ========================== */
522
523               case 'P': /* detailed cost centre profiling (time/alloc) */
524                 COST_CENTRE_USING_BUILD_ONLY(
525                 RtsFlags.CcFlags.doCostCentres++;
526                 )
527               case 'p': /* cost centre profiling (time/alloc) */
528                 COST_CENTRE_USING_BUILD_ONLY(
529                 RtsFlags.CcFlags.doCostCentres++;
530
531                 switch (rts_argv[arg][2]) {
532                   case SORTCC_LABEL:
533                   case SORTCC_TIME:
534                   case SORTCC_ALLOC:
535                         RtsFlags.CcFlags.sortBy = rts_argv[arg][2];
536                     break;
537                   default:
538                         RtsFlags.CcFlags.sortBy = SORTCC_TIME;
539                     break;
540                 }
541                 ) break;
542
543               case 'i': /* serial profiling -- initial timer interval */
544                 COST_CENTRE_USING_BUILD_ONLY(
545                 interval_ticks = (I_) ((atof(rts_argv[arg]+2) * TICK_FREQUENCY));
546                 if (interval_ticks <= 0)
547                     interval_ticks = 1;
548                 ) break;
549
550               case 'h': /* serial heap profile */
551 #if !defined(PROFILING) && defined(DEBUG)
552                 switch (rts_argv[arg][2]) {
553                   case '\0':
554                   case 'L':
555                     RtsFlags.ProfFlags.doHeapProfile = HEAP_BY_INFOPTR;
556                     break;
557                   case 'T':
558                     RtsFlags.ProfFlags.doHeapProfile = HEAP_BY_CLOSURE_TYPE;
559                     break;
560                   default:
561                     fprintf(stderr, "Invalid heap profile option: %s\n",
562                             rts_argv[arg]);
563                     error = rtsTrue;
564                 }
565 #else
566                 PROFILING_BUILD_ONLY(
567                 switch (rts_argv[arg][2]) {
568                   case '\0':
569                   case CCchar:
570                     RtsFlags.ProfFlags.doHeapProfile = HEAP_BY_CC;
571                     break;
572                   case MODchar:
573                     RtsFlags.ProfFlags.doHeapProfile = HEAP_BY_MOD;
574                     break;
575                   case GRPchar:
576                     RtsFlags.ProfFlags.doHeapProfile = HEAP_BY_GRP;
577                     break;
578                   case DESCRchar:
579                     RtsFlags.ProfFlags.doHeapProfile = HEAP_BY_DESCR;
580                     break;
581                   case TYPEchar:
582                     RtsFlags.ProfFlags.doHeapProfile = HEAP_BY_TYPE;
583                     break;
584                   case TIMEchar:
585                     RtsFlags.ProfFlags.doHeapProfile = HEAP_BY_TIME;
586                     if (rts_argv[arg][3]) {
587                         char *start_str = strchr(rts_argv[arg]+3, ',');
588                         I_ intervals;
589                         if (start_str) *start_str = '\0';
590
591                         if ((intervals = decode(rts_argv[arg]+3)) != 0) {
592                             time_intervals = (hash_t) intervals;
593                             /* ToDo: and what if it *is* zero intervals??? */
594                         }
595                         if (start_str) {
596                             earlier_ticks = (I_)((atof(start_str + 1) * TICK_FREQUENCY));
597                         }
598                     }
599                     break;
600                   default:
601                     fprintf(stderr, "Invalid heap profile option: %s\n",
602                             rts_argv[arg]);
603                     error = rtsTrue;
604                 }
605                 ) 
606 #endif
607                 break;
608
609               case 'z': /* size of index tables */
610                 PROFILING_BUILD_ONLY(
611                 switch (rts_argv[arg][2]) {
612                   case CCchar:
613                     max_cc_no = (hash_t) decode(rts_argv[arg]+3);
614                     if (max_cc_no == 0) {
615                         fprintf(stderr, "Bad number of cost centres %s\n", rts_argv[arg]);
616                         error = rtsTrue;
617                     }
618                     break;
619                   case MODchar:
620                     max_mod_no = (hash_t) decode(rts_argv[arg]+3);
621                     if (max_mod_no == 0) {
622                         fprintf(stderr, "Bad number of modules %s\n", rts_argv[arg]);
623                         error = rtsTrue;
624                     }
625                     break;
626                   case GRPchar:
627                     max_grp_no = (hash_t) decode(rts_argv[arg]+3);
628                     if (max_grp_no == 0) {
629                         fprintf(stderr, "Bad number of groups %s\n", rts_argv[arg]);
630                         error = rtsTrue;
631                     }
632                     break;
633                   case DESCRchar:
634                     max_descr_no = (hash_t) decode(rts_argv[arg]+3);
635                     if (max_descr_no == 0) {
636                         fprintf(stderr, "Bad number of closure descriptions %s\n", rts_argv[arg]);
637                         error = rtsTrue;
638                     }
639                     break;
640                   case TYPEchar:
641                     max_type_no = (hash_t) decode(rts_argv[arg]+3);
642                     if (max_type_no == 0) {
643                         fprintf(stderr, "Bad number of type descriptions %s\n", rts_argv[arg]);
644                         error = rtsTrue;
645                     }
646                     break;
647                   default:
648                     fprintf(stderr, "Invalid index table size option: %s\n",
649                             rts_argv[arg]);
650                     error = rtsTrue;
651                 }
652                 ) break;
653
654               case 'c': /* cost centre label select */
655               case 'g': /* cost centre group select */
656               case 'd': /* closure descr select */
657               case 'y': /* closure type select */
658                 PROFILING_BUILD_ONLY(
659                 {char *left  = strchr(rts_argv[arg], '{');
660                  char *right = strrchr(rts_argv[arg], '}');
661
662                 if (! left || ! right ||
663                         strrchr(rts_argv[arg], '{') != left ||
664                          strchr(rts_argv[arg], '}') != right) {
665                     fprintf(stderr, "Invalid heap profiling selection bracketing\n   %s\n", rts_argv[arg]);
666                     error = rtsTrue;
667                 } else {
668                     *right = '\0';
669                     switch (rts_argv[arg][1]) {
670                       case 'c': /* cost centre label select */
671                         RtsFlags.ProfFlags.ccSelector = left + 1;
672                         break;
673                       case 'm': /* cost centre module select */
674                         RtsFlags.ProfFlags.modSelector = left + 1;
675                         break;
676                       case 'g': /* cost centre group select */
677                         RtsFlags.ProfFlags.grpSelector = left + 1;
678                         break;
679                       case 'd': /* closure descr select */
680                         RtsFlags.ProfFlags.descrSelector = left + 1;
681                         break;
682                       case 'y': /* closure type select */
683                         RtsFlags.ProfFlags.typeSelector = left + 1;
684                         break;
685                       case 'k': /* closure kind select */
686                         RtsFlags.ProfFlags.kindSelector = left + 1;
687                         break;
688                     }
689                 }}
690                 ) break;
691
692               /* =========== CONCURRENT ========================= */
693               case 'C': /* context switch interval */
694                 if (rts_argv[arg][2] == '\0')
695                     RtsFlags.ConcFlags.ctxtSwitchTime = 0;
696                 else {
697                     I_ cst; /* tmp */
698
699                     /* Convert to milliseconds */
700                     cst = (I_) ((atof(rts_argv[arg]+2) * 1000));
701                     cst = (cst / CS_MIN_MILLISECS) * CS_MIN_MILLISECS;
702                     if (cst < CS_MIN_MILLISECS)
703                         cst = CS_MIN_MILLISECS;
704
705                     RtsFlags.ConcFlags.ctxtSwitchTime = cst;
706                 }
707                 break;
708
709               case 't':
710                 if (rts_argv[arg][2] != '\0') {
711                     RtsFlags.ConcFlags.maxThreads
712                       = strtol(rts_argv[arg]+2, (char **) NULL, 10);
713                 } else {
714                     fprintf(stderr, "setupRtsFlags: missing size for -t\n");
715                     error = rtsTrue;
716                 }
717                 break;
718
719               /* =========== PARALLEL =========================== */
720               case 'e':
721                 PAR_BUILD_ONLY(
722                 if (rts_argv[arg][2] != '\0') { /* otherwise, stick w/ the default */
723
724                     RtsFlags.ParFlags.maxLocalSparks
725                       = strtol(rts_argv[arg]+2, (char **) NULL, 10);
726
727                     if (RtsFlags.ParFlags.maxLocalSparks <= 0) {
728                         fprintf(stderr, "setupRtsFlags: bad value for -e\n");
729                         error = rtsTrue;
730                     }
731                 }
732                 ) break;
733
734               case 'O':
735                 PAR_BUILD_ONLY(
736                 RtsFlags.ParFlags.outputDisabled = rtsTrue;
737                 ) break;
738
739               case 'q': /* activity profile option */
740                 PAR_BUILD_ONLY(
741                 if (rts_argv[arg][2] == 'b')
742                     RtsFlags.ParFlags.granSimStats_Binary = rtsTrue;
743                 else
744                     RtsFlags.ParFlags.granSimStats = rtsTrue;
745                 ) break;
746
747 #if 0 /* or??? */
748               case 'q': /* quasi-parallel profile option */
749                 GRAN_BUILD_ONLY (
750                 if (rts_argv[arg][2] == 'v')
751                     do_qp_prof = 2;
752                 else
753                     do_qp_prof++;
754                 ) break;
755 #endif /* 0??? */
756
757               case 'Q': /* Set pack buffer size */
758                 PAR_BUILD_ONLY(
759                 if (rts_argv[arg][2] != '\0') {
760                     RtsFlags.ParFlags.packBufferSize = decode(rts_argv[arg]+2);
761                 } else {
762                     fprintf(stderr, "setupRtsFlags: missing size of PackBuffer (for -Q)\n");
763                     error = rtsTrue;
764                 }
765                 ) break;
766
767               /* =========== GRAN =============================== */
768
769               case 'b':
770                 GRAN_BUILD_ONLY(
771                 process_gran_option(arg, rts_argc, rts_argv, &error);
772                 ) break;
773
774               /* =========== TICKY ============================== */
775
776               case 'r': /* Basic profiling stats */
777                 TICKY_BUILD_ONLY(
778
779                 RtsFlags.TickyFlags.showTickyStats = rtsTrue;
780                 RtsFlags.TickyFlags.tickyFile
781                   = open_stats_file(arg, *argc, argv,
782                         *rts_argc, rts_argv, TICKY_FILENAME_FMT);
783
784                 if (RtsFlags.TickyFlags.tickyFile == NULL) error = rtsTrue;
785                 ) break;
786
787               /* =========== OH DEAR ============================ */
788               default:
789                 fprintf(stderr, "setupRtsFlags: Unknown RTS option: %s\n",rts_argv[arg]);
790                 error = rtsTrue;
791                 break;
792             }
793         }
794     }
795     if (error) {
796         const char **p;
797
798         fflush(stdout);
799         for (p = usage_text; *p; p++)
800             fprintf(stderr, "%s\n", *p);
801         stg_exit(EXIT_FAILURE);
802     }
803
804 }
805
806 static FILE *           /* return NULL on error */
807 open_stats_file (
808     I_ arg,
809     int argc, char *argv[],
810     int rts_argc, char *rts_argv[],
811     const char *FILENAME_FMT)
812 {
813     FILE *f = NULL;
814
815     if (strequal(rts_argv[arg]+2, "stderr")) /* use real stderr */
816         f = stderr;
817     else if (rts_argv[arg][2] != '\0')      /* stats file specified */
818         f = fopen(rts_argv[arg]+2,"w");
819     else {
820         char stats_filename[STATS_FILENAME_MAXLEN]; /* default <program>.<ext> */
821         sprintf(stats_filename, FILENAME_FMT, argv[0]);
822         f = fopen(stats_filename,"w");
823     }
824     if (f == NULL) {
825         fprintf(stderr, "Can't open stats file %s\n", rts_argv[arg]+2);
826     } else {
827         /* Write argv and rtsv into start of stats file */
828         I_ count;
829         for(count = 0; count < argc; count++)
830             fprintf(f, "%s ", argv[count]);
831         fprintf(f, "+RTS ");
832         for(count = 0; count < rts_argc; count++)
833             fprintf(f, "%s ", rts_argv[count]);
834         fprintf(f, "\n");
835     }
836
837     return(f);
838 }
839
840 static I_
841 decode(const char *s)
842 {
843     I_ c;
844     StgDouble m;
845
846     if (!*s)
847         return 0;
848
849     m = atof(s);
850     c = s[strlen(s)-1];
851
852     if (c == 'g' || c == 'G')
853         m *= 1000*1000*1000;    /* UNchecked! */
854     else if (c == 'm' || c == 'M')
855         m *= 1000*1000;                 /* We do not use powers of 2 (1024) */
856     else if (c == 'k' || c == 'K')      /* to avoid possible bad effects on */
857         m *= 1000;                      /* a direct-mapped cache.           */ 
858     else if (c == 'w' || c == 'W')
859         m *= sizeof(W_);
860
861     return (I_)m;
862 }
863
864 static void
865 bad_option(const char *s)
866 {
867   fflush(stdout);
868   fprintf(stderr, "initSM: Bad RTS option: %s\n", s);
869   stg_exit(EXIT_FAILURE);
870 }