[project @ 1996-01-08 20:28:12 by partain]
[ghc-hetmet.git] / ghc / runtime / main / main.lc
diff --git a/ghc/runtime/main/main.lc b/ghc/runtime/main/main.lc
new file mode 100644 (file)
index 0000000..98002a3
--- /dev/null
@@ -0,0 +1,1355 @@
+%/****************************************************************
+%*                                                             *
+%*     This is where everything starts                         *
+%*                                                             *
+%****************************************************************/
+
+\begin{code}
+#if defined(USE_COST_CENTRES) || defined(GUM) || defined(CONCURRENT)
+#define NON_POSIX_SOURCE /* time things on Solaris -- sigh */
+#endif
+
+#include "rtsdefs.h"
+#include <setjmp.h>
+
+#if defined(STDC_HEADERS) || defined(HAVE_STRING_H)
+# include <string.h>
+/* An ANSI string.h and pre-ANSI memory.h might conflict.  */
+# if !defined(STDC_HEADERS) && defined(HAVE_MEMORY_H)
+#  include <memory.h>
+# endif /* not STDC_HEADERS and HAVE_MEMORY_H */
+# define index strchr
+# define rindex strrchr
+# define bcopy(s, d, n) memcpy ((d), (s), (n))
+# define bcmp(s1, s2, n) memcmp ((s1), (s2), (n))
+# define bzero(s, n) memset ((s), 0, (n))
+#else /* not STDC_HEADERS and not HAVE_STRING_H */
+# include <strings.h>
+/* memory.h and strings.h conflict on some systems.  */
+#endif /* not STDC_HEADERS and not HAVE_STRING_H */
+
+#if defined(USE_COST_CENTRES) || defined(GUM)
+/* need some "time" things */
+
+/* ToDo: This is a mess! Improve ? */
+
+# ifdef HAVE_SYS_TYPES_H
+#  include <sys/types.h>
+# endif
+
+# ifdef HAVE_SYS_TIMES_H
+#  include <sys/times.h>
+# endif
+
+# ifdef HAVE_SYS_TIME_H
+#  include <sys/time.h>
+# endif
+#endif /* USE_COST_CENTRES || GUM */
+
+#ifndef PAR
+STGRegisterTable MainRegTable;
+#endif
+
+/* fwd decls */
+void setupRtsFlags PROTO((int *argc, char *argv[], I_ *rtsc, char *rtsv[]));
+void shutdownHaskell(STG_NO_ARGS);
+
+EXTFUN(startStgWorld);
+extern void PrintRednCountInfo(STG_NO_ARGS);
+extern void checkAStack(STG_NO_ARGS);
+
+/* a real nasty Global Variable */
+/* moved to main/TopClosure(13)?.lc -- *one* of them will get linked in
+P_ TopClosure = Main_mainPrimIO_closure;
+ */
+
+/* structure to carry around info about the storage manager */
+smInfo StorageMgrInfo;
+
+FILE *main_statsfile = NULL;
+#if defined(DO_REDN_COUNTING)
+FILE *tickyfile = NULL;
+#endif
+#if defined(SM_DO_BH_UPDATE)
+I_ noBlackHoles = 0;
+#endif
+I_ doSanityChks = 0;
+I_ showRednCountStats = 0;
+I_ traceUpdates = 0;
+extern I_ squeeze_upd_frames;
+
+#ifdef PAR
+extern I_      OkToGC, buckets, average_stats();
+extern rtsBool TraceSparks, OutputDisabled, DelaySparks,
+               DeferGlobalUpdates, ParallelStats;
+
+extern void RunParallelSystem PROTO((P_));
+extern void initParallelSystem(STG_NO_ARGS);
+extern void SynchroniseSystem(STG_NO_ARGS);
+
+extern void SetTrace PROTO((W_ address, I_ level/*?*/));
+#endif
+
+#if defined(GRAN_CHECK) && defined(GRAN)
+extern W_ debug;
+extern W_ event_trace ;
+extern W_ event_trace_all ;
+#endif
+
+extern void *stgAllocForGMP   PROTO((size_t));
+extern void *stgReallocForGMP PROTO ((void *, size_t, size_t));
+extern void  stgDeallocForGMP PROTO ((void *, size_t));
+
+#if defined (DO_SPAT_PROFILING) && sparc_TARGET_ARCH
+    /* NOTE: I, WDP, do not use this in my SPAT profiling */
+W_ KHHP, KHHPLIM, KHSPA, KHSPB;
+#endif
+
+/* NeXTs can't just reach out and touch "end", to use in
+   distinguishing things in static vs dynamic (malloc'd) memory.
+*/
+#if nextstep2_TARGET_OS || nextstep3_TARGET_OS /* ToDo: use END_BY_FUNNY_MEANS or something */
+void *get_end_result;
+#endif
+
+I_    prog_argc;
+char  **prog_argv;
+I_    rts_argc;
+char *rts_argv[MAX_RTS_ARGS];
+
+#ifndef PAR
+jmp_buf restart_main;      /* For restarting after a signal */
+#endif
+
+#if defined(PVM)
+unsigned nPEs = 0, nIMUs = 0;
+#endif
+
+#if defined(GUM)
+int nPEs = 0;
+#endif
+
+int /* return type of "main" is defined by the C standard */
+main(argc, argv)
+    int argc;
+    char *argv[];
+{
+\end{code}
+
+The very first thing we do is grab the start time...just in case we're
+collecting timing statistics.
+
+\begin{code}
+
+    start_time();
+
+\end{code}
+
+The parallel system needs to be initialised and synchronised before
+the program is run.  This is done {\em before} heap allocation, so we
+can grab all remaining heap without needing to consider the System
+Manager's requirements.
+
+\begin{code}
+#ifdef PAR
+    /* 
+     * Grab the number of PEs out of the argument vector, and eliminate it
+     * from further argument processing
+     */
+    nPEs = atoi(argv[1]);
+    argv[1] = argv[0];
+    argv++;
+    argc--;
+
+/*    fprintf(stderr, "I'm alive, nPEs = %d \n", nPEs);    */
+    SynchroniseSystem();
+#endif
+
+#if defined(USE_COST_CENTRES) || defined(GUM)
+    /* setup string indicating time of run -- only used for profiling */
+    (void) time_str();
+#endif
+
+#if nextstep2_TARGET_OS || nextstep3_TARGET_OS /* ToDo: use END_BY_FUNNY_MEANS or something */
+    get_end_result = get_end();
+#endif
+
+    /* 
+       divide the command-line args between pgm and RTS;
+       figure out what statsfile to use (if any);
+       [if so, write the whole cmd-line into it]
+       
+       This is unlikely to work well in parallel!  KH.
+    */
+    setupRtsFlags(&argc, argv, &rts_argc, rts_argv);
+    prog_argc = argc;
+    prog_argv = argv;
+
+#if defined(PAR)
+   /* Initialise the parallel system -- before initHeap! */
+   initParallelSystem();
+#endif /* PAR */
+
+#if defined(LIFE_PROFILE)
+    if (life_profile_init(rts_argv, prog_argv) != 0) {
+        fflush(stdout);
+       fprintf(stderr, "life_profile_init failed!\n");
+       EXIT(EXIT_FAILURE);
+    }
+#endif
+
+#if defined(USE_COST_CENTRES) || defined(GUM)
+    if (init_cc_profiling(rts_argc, rts_argv, prog_argv) != 0) {
+        fflush(stdout);
+       fprintf(stderr, "init_cc_profiling failed!\n");
+       EXIT(EXIT_FAILURE);
+    }
+#endif
+
+#if defined(CONCURRENT) && defined(GRAN)
+    if (!no_gr_profile)
+      if (init_gr_simulation(rts_argc, rts_argv, prog_argc, prog_argv) != 0) {
+         fprintf(stderr, "init_gr_simulation failed!\n"); EXIT(EXIT_FAILURE);
+      }
+#endif
+
+#ifdef PAR
+    if (do_gr_profile)
+       init_gr_profiling(rts_argc, rts_argv, prog_argc, prog_argv);
+#endif
+
+    /* 
+       initialize the storage manager
+    */
+    if ( initSM(rts_argc, rts_argv, main_statsfile) != 0) {
+        fflush(stdout);
+       fprintf(stderr, "initSM failed!\n");
+       EXIT(EXIT_FAILURE);
+    }
+
+#ifndef PAR
+    if ( initStacks( &StorageMgrInfo ) != 0) {
+        fflush(stdout);
+       fprintf(stderr, "initStacks failed!\n");
+       EXIT(EXIT_FAILURE);
+    }
+#endif
+
+    if ( initHeap( &StorageMgrInfo ) != 0) {
+        fflush(stdout);
+       fprintf(stderr, "initHeap failed!\n"); EXIT(EXIT_FAILURE);
+    }
+
+#if defined(CONCURRENT) && !defined(GRAN)
+    if (!initThreadPools(MaxLocalSparks)) {
+        fflush(stdout);
+       fprintf(stderr, "initThreadPools failed!\n"); 
+        EXIT(EXIT_FAILURE);
+    }
+#endif
+
+#if defined(USE_COST_CENTRES) || defined(GUM)
+    /* call cost centre registering routine (after heap allocated) */
+    cc_register();
+#endif
+
+/* Information needed by runtime trace analysers -- don't even ask what it does! */
+  /* NOTE: I, WDP, do not use this in my SPAT profiling */
+#if defined (DO_SPAT_PROFILING) && sparc_TARGET_ARCH
+   KHHPLIM = (W_) StorageMgrInfo.hplim;
+   KHHP =    (W_) StorageMgrInfo.hp;
+   KHSPA =   (W_) SAVE_SpA,
+   KHSPB =   (W_) SAVE_SpB;
+
+/*  fprintf(stderr,"Hp = %lx, HpLim = %lx, SpA = %lx, SpB = %lx\n",KHHP,KHHPLIM,KHSPA,KHSPB); */
+
+/* NOT ME:
+  __asm__("sethi %%hi(_KHHP),%%o0\n\tld [%%o0+%%lo(_KHHP)],%%g0" : : : "%%o0");
+  __asm__("sethi %%hi(_KHHPLIM),%%o0\n\tld [%%o0+%%lo(_KHHPLIM)],%%g0" : : : "%%o0");
+  __asm__("sethi %%hi(_KHSPA),%%o0\n\tld [%%o0+%%lo(_KHSPA)],%%g0" : : : "%%o0");
+  __asm__("sethi %%hi(_KHSPB),%%o0\n\tld [%%o0+%%lo(_KHSPB)],%%g0" : : : "%%o0");
+*/
+#endif
+
+#if defined(DO_REDN_COUNTING)
+    max_SpA = MAIN_SpA; /* initial high-water marks */
+    max_SpB = MAIN_SpB;
+#endif
+
+    /* Tell GNU multi-precision pkg about our custom alloc functions */
+    mp_set_memory_functions(stgAllocForGMP, stgReallocForGMP, stgDeallocForGMP);
+
+    /* Record initialization times */
+    end_init();
+
+#if defined(USE_COST_CENTRES) || defined(CONCURRENT)
+    /* 
+     * Both the context-switcher and the cost-center profiler use 
+     * a virtual timer.
+     */
+    if (install_vtalrm_handler()) {
+       fflush(stdout);
+       fprintf(stderr, "Can't install VTALRM handler.\n");
+       EXIT(EXIT_FAILURE);
+    }
+#if (defined(CONCURRENT) && defined(USE_COST_CENTRES)) || defined(GUM)
+    if (time_profiling) {
+       if (contextSwitchTime % (1000/TICK_FREQUENCY) == 0)
+           tick_millisecs = TICK_MILLISECS;
+       else
+           tick_millisecs = CS_MIN_MILLISECS;
+
+       contextSwitchTicks = contextSwitchTime / tick_millisecs;
+       profilerTicks = TICK_MILLISECS / tick_millisecs;
+    } else
+       tick_millisecs = contextSwitchTime;
+#endif
+
+#ifndef CONCURRENT
+    START_TIME_PROFILER;
+#endif
+
+#endif /* USE_COST_CENTRES || CONCURRENT */
+
+#ifndef PAR
+    setjmp(restart_main);
+    initUserSignals();
+#endif
+
+#ifdef CONCURRENT
+# if defined(GRAN)                                                 /* HWL */
+    /* RunnableThreadsHd etc. are init in ScheduleThreads */
+    /* 
+     * I'm not sure about this.  Note that this code is for re-initializing
+     * things when a longjmp to restart_main occurs.  --JSM
+     */
+
+# else                                                             /* !GRAN */
+    AvailableStack = AvailableTSO = Nil_closure;
+    RunnableThreadsHd = RunnableThreadsTl = Nil_closure;
+    WaitingThreadsHd = WaitingThreadsTl = Nil_closure;
+    PendingSparksHd[REQUIRED_POOL] = 
+      PendingSparksTl[REQUIRED_POOL] = PendingSparksBase[REQUIRED_POOL];
+    PendingSparksHd[ADVISORY_POOL] = 
+      PendingSparksTl[ADVISORY_POOL] = PendingSparksBase[ADVISORY_POOL];
+# endif
+
+    CurrentTSO = Nil_closure;
+
+# ifdef PAR
+    RunParallelSystem(TopClosure);
+# else
+    STKO_LINK(MainStkO) = Nil_closure;
+    ScheduleThreads(TopClosure);
+# endif        /* PAR */
+
+#else  /* not threaded (sequential) */
+
+# if defined(__STG_TAILJUMPS__)
+    miniInterpret((StgFunPtr)startStgWorld);
+# else
+    if (doSanityChks)
+       miniInterpret_debug((StgFunPtr)startStgWorld, checkAStack);
+    else
+       miniInterpret((StgFunPtr)startStgWorld);
+# endif /* not tail-jumping */
+#endif /* !CONCURRENT */
+
+    shutdownHaskell();
+    return(EXIT_SUCCESS);    /* don't use EXIT! :-) */
+}
+\end{code}
+
+It should be possible to call @shutdownHaskell@ whenever you want to
+shut a Haskell program down in an orderly way.
+
+Note that some of this code probably depends on the integrity of
+various internal data structures so this should not be called in
+response to detecting a catastrophic error.
+
+\begin{code}
+void
+shutdownHaskell(STG_NO_ARGS)
+{
+    STOP_TIME_PROFILER;
+
+    if (exitSM(&StorageMgrInfo) != 0) {
+       fflush(stdout);
+       fprintf(stderr, "exitSM failed!\n");
+       EXIT(EXIT_FAILURE);
+    }
+#if defined(LIFE_PROFILE)
+    {
+       extern P_ hp_start;     /* from the SM -- Hack! */
+       life_profile_finish(StorageMgrInfo.hp - hp_start, prog_argv);
+    }
+#endif
+
+#if defined(USE_COST_CENTRES)
+    heap_profile_finish();
+#endif
+#if defined(USE_COST_CENTRES) || defined(GUM)
+    report_cc_profiling(1 /* final */ );
+#endif
+
+#if defined(DO_REDN_COUNTING)
+    if (showRednCountStats) {
+       PrintRednCountInfo();
+    }
+#endif
+
+#if defined(GRAN_CHECK) && defined(GRAN)
+    if (PrintFetchMisses)
+      fprintf(stderr,"Number of fetch misses: %d\n",fetch_misses);
+
+# if defined(COUNT)
+    fprintf(stderr,"COUNT statistics:\n");
+    fprintf(stderr,"  Total number of updates: %u\n",nUPDs);
+    fprintf(stderr,"  Needed to awaken BQ: %u with avg BQ len of: %f\n",
+           nUPDs_BQ,(float)BQ_lens/(float)nUPDs_BQ);
+    fprintf(stderr,"  Number of PAPs: %u\n",nPAPs);
+# endif
+
+    if (!no_gr_profile)
+      end_gr_simulation();
+#endif
+
+    fflush(stdout);
+    /* This fflush is important, because: if "main" just returns,
+       then we will end up in pre-supplied exit code that will close
+       streams and flush buffers.  In particular we have seen: it
+       will close fd 0 (stdin), then flush fd 1 (stdout), then <who
+       cares>...
+
+       But if you're playing with sockets, that "close fd 0" might
+       suggest to the daemon that all is over, only to be presented
+       with more stuff on "fd 1" at the flush.
+
+       The fflush avoids this sad possibility.
+    */
+}
+\end{code}
+
+%/****************************************************************
+%*                                                             *
+%*         Getting default settings for RTS parameters         *
+%*                                                             *
+%* +RTS indicates following arguments destined for RTS          *
+%* -RTS indicates following arguments destined for program      *
+%*                                                              *
+%****************************************************************/
+\begin{code}
+
+char *flagtext[] = {
+"",
+"Usage: <prog> <args> [+RTS <rtsopts> | -RTS <args>] ... --RTS <args>",
+"",
+"   +RTS    Indicates run time system options follow",
+"   -RTS    Indicates program arguments follow",
+"  --RTS    Indicates that ALL subsequent arguments will be given to the",
+"           program (including any of these RTS flags)",
+"",
+"The following run time system options are available:",
+"",
+"  -? -f    Prints this message and exits; the program is not executed",
+"",
+"  -K<size> Sets the stack size (default 64k)    Egs: -K32k   -K512k",
+"  -H<size> Sets the heap size  (default 4M)          -H512k  -H16M",
+"  -s<file> Summary GC statistics   (default file: <program>.stat)",
+"  -S<file> Detailed GC statistics  (with -Sstderr going to stderr)",
+"",
+#if defined(GCap)
+"  -M<n>%   Sets minimum size of alloc area as % of heap (default 3%)",
+"  -A<size> Fixes size of alloc area, overriding any minimum (-A gives 64k)",
+"  -G<size> Fixes size of major generation (default is dynamic threshold)",
+"  -F2s     Forces program compiled for Appel gc to use 2s collection",
+#else
+# if defined(GCgn)
+"  -A<size> Specifies size of alloc area (default 64k)",
+"  -G<size> Fixes size of major generation (default is available heap)",
+"  -F2s     Forces program compiled for Gen gc to use 2s collection",
+# else
+"  -M<n>%   Minimum % of heap which must be available (default 3%)",
+"  -A<size> Fixes size of heap area allocated between GCs (-A gives 64k)",
+# endif
+#endif
+#if defined(FORCE_GC)
+"  -j<size>  Forces major GC at every <size> bytes allocated",
+#endif /* FORCE_GC */
+#if defined(GCdu)
+"  -u<percent> Fixes residency threshold at which mode switches (range 0.0..0.95)",
+#endif
+"",
+#if defined(SM_DO_BH_UPDATE)
+"  -N       No black-holing (for use when a signal handler is present)",
+#endif
+"  -Z       Don't squeeze out update frames on stack overflow",
+"  -B      Sound the bell at the start of each (major) garbage collection",
+#if defined(USE_COST_CENTRES) || defined(GUM)
+"",
+"  -p<sort> Produce cost centre time profile  (output file <program>.prof)",
+"             sort: T = time (default), A = alloc, C = cost centre label",
+"  -P<sort> Produce serial time profile (output file <program>.time)",
+"             and a -p profile with detailed caf/enter/tick/alloc info",
+#if defined(USE_COST_CENTRES)
+"",
+"  -h<break-down> Heap residency profile      (output file <program>.hp)",
+"     break-down: C = cost centre (default), M = module, G = group",
+"                 D = closure description, Y = type description",
+"                 T<ints>,<start> = time closure created",
+"                    ints:  no. of interval bands plotted (default 18)",
+"                    start: seconds after which intervals start (default 0.0)",
+"  A subset of closures may be selected by the attached cost centre using:",
+"    -c{mod:lab,mod:lab...}, specific module:label cost centre(s)",
+"    -m{mod,mod...} all cost centres from the specified modules(s)",
+"    -g{grp,grp...} all cost centres from the specified group(s)",
+"  Selections can also be made by description, type, kind and age:",
+"    -d{des,des...} closures with specified closure descriptions",
+"    -y{typ,typ...} closures with specified type descriptions",
+"    -k{knd,knd...} closures of the specified kinds",
+"    -a<age>        closures which survived <age> complete intervals",
+"  The selection logic used is summarised as follows:",
+"    ([-c] or [-m] or [-g]) and ([-d] or [-y] or [-k]) and [-a]",
+"    where an option is true if not specified",
+#endif
+"",
+"  -z<tbl><size>  set hash table <size> for <tbl> (C, M, G, D or Y)",
+"",
+"  -i<secs> Number of seconds in a profiling interval (default 1.0):",
+"           heap profile (-h) and/or serial time profile (-P) frequency",
+#endif /* USE_COST_CENTRES */
+#if defined(LIFE_PROFILE)
+"",
+"  -l<res>  Produce liftime and update profile (output file <program>.life)",
+"              res: the age resolution in bytes allocated   (default 10,000)",
+#endif /* LIFE_PROFILE */
+"",
+#if defined(DO_REDN_COUNTING)
+"  -r<file>  Produce reduction profiling statistics (with -rstderr for stderr)",
+"",
+#endif
+"  -I       Use debugging miniInterpret with stack and heap sanity-checking.",
+"  -T<level> Trace garbage collection execution (debugging)",
+#ifdef CONCURRENT
+"",
+# ifdef PAR
+"  -N<n>     Use <n> PVMish processors in parallel (default: 2)",
+/* NB: the -N<n> is implemented by the driver!! */
+# endif
+"  -C<secs>  Context-switch interval in seconds",
+"                (0 or no argument means switch as often as possible)",
+"                the default is .01 sec; resolution is .01 sec",
+"  -e<size>        Size of spark pools (default 100)",
+# ifdef PAR
+"  -q        Enable activity profile (output files in ~/<program>*.gr)",
+"  -qb       Enable binary activity profile (output file /tmp/<program>.gb)",
+#else
+"  -q[v]     Enable quasi-parallel profile (output file <program>.qp)",
+# endif
+"  -t<num>   Set maximum number of advisory threads per PE (default 32)",
+"  -o<num>   Set stack chunk size (default 1024)",
+# ifdef PAR
+"  -d        Turn on PVM-ish debugging",
+"  -O        Disable output for performance measurement",
+# endif /* PAR */
+#endif /* CONCURRENT */
+"",
+"Other RTS options may be available for programs compiled a different way.",
+"The GHC User's Guide has full details.",
+"",
+0
+};
+
+#define RTS 1
+#define PGM 0
+
+#ifndef atof
+extern double atof();
+/* no proto because some machines use const and some do not */
+#endif
+
+void
+setupRtsFlags(argc, argv, rts_argc, rts_argv)
+int *argc;
+I_ *rts_argc;
+char *argv[], *rts_argv[];
+{
+    I_ error = 0;
+    I_ mode;
+    I_ arg, total_arg;
+    char *last_slash;
+
+    /* Remove directory from argv[0] -- default files in current directory */
+
+    if ((last_slash = (char *) rindex(argv[0], '/')) != NULL)
+       strcpy(argv[0], last_slash+1);
+
+    /* Split arguments (argv) into PGM (argv) and RTS (rts_argv) parts */
+    /*   argv[0] must be PGM argument -- leave in argv                 */
+
+    total_arg = *argc;
+    arg = 1;
+
+    *argc = 1;
+    *rts_argc = 0;
+
+    for (mode = PGM; arg < total_arg && strcmp("--RTS", argv[arg]) != 0; arg++) {
+       if (strcmp("+RTS", argv[arg]) == 0) {
+           mode = RTS;
+       }
+       else if (strcmp("-RTS", argv[arg]) == 0) {
+           mode = PGM;
+       }
+       else if (mode == RTS && *rts_argc < MAX_RTS_ARGS-1) {
+           rts_argv[(*rts_argc)++] = argv[arg];
+       }
+       else if (mode == PGM) {
+           argv[(*argc)++] = argv[arg];
+       }
+       else {
+           fflush(stdout);
+           fprintf(stderr, "setupRtsFlags: Too many RTS arguments (max %d)\n",
+                   MAX_RTS_ARGS-1);
+           EXIT(EXIT_FAILURE);
+       }
+    }
+    if (arg < total_arg) {
+       /* arg must be --RTS; process remaining program arguments */
+       while (++arg < total_arg) {
+           argv[(*argc)++] = argv[arg];
+       }
+    }
+    argv[*argc] = (char *) 0;
+    rts_argv[*rts_argc] = (char *) 0;
+
+    /* Process RTS (rts_argv) part: mainly to determine statsfile */
+
+    for (arg = 0; arg < *rts_argc; arg++) {
+       if (rts_argv[arg][0] == '-') {
+           switch(rts_argv[arg][1]) {
+             case '?':
+             case 'f':
+               error = 1;
+               break;
+
+             case 'Z': /* Don't squeeze out update frames */
+                   squeeze_upd_frames = 0;
+               break;
+
+#if defined(SM_DO_BH_UPDATE)
+             case 'N':
+               noBlackHoles++;
+               break;
+#endif
+
+             case 'I':
+               doSanityChks++;
+#if defined(__STG_TAILJUMPS__)
+               /* Blech -- too many errors if run in parallel -- KH */
+               fprintf(stderr, "setupRtsFlags: Using Tail Jumps: Sanity checks not possible: %s\n", rts_argv[arg]);
+               error = 1;
+#endif
+               break;
+
+             case 'U':
+               traceUpdates++;
+#if ! defined(DO_RUNTIME_TRACE_UPDATES)
+               fprintf(stderr, "setupRtsFlags: Update Tracing not compiled in: %s\n", rts_argv[arg]);
+               error = 1;
+#endif
+               break;
+
+             case 'r': /* Basic profiling stats */
+               showRednCountStats++;
+#if ! defined(DO_REDN_COUNTING)
+               fprintf(stderr, "setupRtsFlags: Reduction counting not compiled in: %s\n", rts_argv[arg]);
+               error = 1;
+
+#else /* ticky-ticky! */
+               if (strcmp(rts_argv[arg]+2, "stderr") == 0) /* use real stderr */
+                   tickyfile = stderr;
+               else if (rts_argv[arg][2] != '\0')          /* ticky file specified */
+                   tickyfile = fopen(rts_argv[arg]+2,"w");
+               else {
+                   char stats_filename[STATS_FILENAME_MAXLEN]; /* default <program>.ticky */
+                   sprintf(stats_filename, TICKY_FILENAME_FMT, argv[0]);
+                   tickyfile = fopen(stats_filename,"w");
+               }
+               if (tickyfile == NULL) {
+                   fprintf(stderr, "Can't open tickyfile %s (default %0.24s.ticky)\n",
+                               rts_argv[arg]+2, argv[0]);
+                   error = 1;
+               } else {
+                   /* Write argv and rtsv into start of ticky file */
+                   I_ count;
+                   for(count = 0; count < *argc; count++)
+                       fprintf(tickyfile, "%s ", argv[count]);
+                   fprintf(tickyfile, "+RTS ");
+                   for(count = 0; count < *rts_argc; count++)
+                       fprintf(tickyfile, "%s ", rts_argv[count]);
+                   fprintf(tickyfile, "\n");
+               }
+#endif /* ticky-ticky! */
+               break;
+
+             case 's': /* Also used by GC -- open file here */
+             case 'S':
+#ifdef PAR
+               /* Opening all those files would almost certainly fail... */
+               ParallelStats = rtsTrue;
+               main_statsfile = stderr; /* temporary; ToDo: rm */
+#else
+               if (strcmp(rts_argv[arg]+2, "stderr") == 0)       /* use real stderr */
+                   main_statsfile = stderr;
+               else if (rts_argv[arg][2] != '\0')                /* stats file specified */
+                   main_statsfile = fopen(rts_argv[arg]+2,"w");
+               else {
+                   char stats_filename[STATS_FILENAME_MAXLEN]; /* default <program>.stat */
+                   sprintf(stats_filename, STAT_FILENAME_FMT, argv[0]);
+                   main_statsfile = fopen(stats_filename,"w");
+               }
+               if (main_statsfile == NULL) {
+                   fprintf(stderr, "Can't open statsfile %s (default %0.24s.stat)\n", rts_argv[arg]+2, argv[0]);
+                   error = 1;
+               } else {
+                   /* Write argv and rtsv into start of stats file */
+                   I_ count;
+                   for(count = 0; count < *argc; count++)
+                       fprintf(main_statsfile, "%s ", argv[count]);
+                   fprintf(main_statsfile, "+RTS ");
+                   for(count = 0; count < *rts_argc; count++)
+                       fprintf(main_statsfile, "%s ", rts_argv[count]);
+                   fprintf(main_statsfile, "\n");
+               }
+#endif
+               break;
+
+             case 'P': /* detailed cost centre profiling (time/alloc) */
+             case 'p': /* cost centre profiling (time/alloc) */
+             case 'i': /* serial profiling -- initial timer interval */
+#if ! (defined(USE_COST_CENTRES) || defined(GUM))
+               fprintf(stderr, "setupRtsFlags: Not built for cost centre profiling: %s\n", rts_argv[arg]);
+               error = 1;
+#endif /* ! (USE_COST_CENTRES || GUM) */
+               break;
+             case 'h': /* serial heap profile */
+             case 'z': /* size of index tables */
+             case 'c': /* cost centre label select */
+             case 'm': /* cost centre module select */
+             case 'g': /* cost centre group select */
+             case 'd': /* closure descr select */
+             case 'y': /* closure type select */
+             case 'k': /* closure kind select */
+             case 'a': /* closure age select */
+#if ! defined(USE_COST_CENTRES)
+               fprintf(stderr, "setupRtsFlags: Not built for cost centre profiling: %s\n", rts_argv[arg]);
+               error = 1;
+#endif /* ! USE_COST_CENTRES */
+               break;
+
+             case 'j': /* force GC option */
+#if defined(FORCE_GC)
+               force_GC++;
+               if (rts_argv[arg][2]) {
+                   GCInterval = decode(rts_argv[arg]+2) / sizeof(W_);
+               }
+#else  /* ! FORCE_GC */
+               fprintf(stderr, "setupRtsFlags: Not built for forcing GC: %s\n", rts_argv[arg]);
+               error = 1;
+#endif /* ! FORCE_GC */
+               break;
+
+             case 'l': /* life profile option */
+#if defined(LIFE_PROFILE)
+               do_life_prof++;
+               if (rts_argv[arg][2]) {
+                   LifeInterval = decode(rts_argv[arg]+2) / sizeof(W_);
+               }
+#else  /* ! LIFE_PROFILE */
+               fprintf(stderr, "setupRtsFlags: Not built for lifetime profiling: %s\n", rts_argv[arg]);
+               error = 1;
+#endif /* ! LIFE_PROFILE */
+               break;
+
+             /* Flags for the threaded RTS */
+
+#ifdef CONCURRENT
+             case 'C': /* context switch interval */
+               if (rts_argv[arg][2] != '\0') {
+                   /* Convert to milliseconds */
+                   contextSwitchTime = (I_) ((atof(rts_argv[arg]+2) * 1000));
+                   contextSwitchTime = (contextSwitchTime / CS_MIN_MILLISECS)
+                                       * CS_MIN_MILLISECS;
+                   if (contextSwitchTime < CS_MIN_MILLISECS)
+                       contextSwitchTime = CS_MIN_MILLISECS;
+               } else
+                   contextSwitchTime = 0;
+               break;
+#if !defined(GRAN)
+             case 'e':
+               if (rts_argv[arg][2] != '\0') {
+                   MaxLocalSparks = strtol(rts_argv[arg]+2, (char **) NULL, 10);
+                   if (MaxLocalSparks <= 0) {
+                       fprintf(stderr, "setupRtsFlags: bad value for -e\n");
+                       error = 1;
+                   }
+               } else
+                   MaxLocalSparks = DEFAULT_MAX_LOCAL_SPARKS;
+               break;
+#endif
+#ifdef PAR
+             case 'q': /* activity profile option */
+               if (rts_argv[arg][2] == 'b')
+                   do_gr_binary++;
+               else
+                   do_gr_profile++;
+               break;
+#else
+             case 'q': /* quasi-parallel profile option */
+               if (rts_argv[arg][2] == 'v')
+                   do_qp_prof = 2;
+               else
+                   do_qp_prof++;
+               break;
+#endif
+             case 't':
+               if (rts_argv[arg][2] != '\0') {
+                   MaxThreads = strtol(rts_argv[arg]+2, (char **) NULL, 10);
+               } else {
+                   fprintf(stderr, "setupRtsFlags: missing size for -t\n");
+                   error = 1;
+               }
+               break;
+
+             case 'o':
+               if (rts_argv[arg][2] != '\0') {
+                   StkOChunkSize = decode(rts_argv[arg]+2);
+                   if (StkOChunkSize < MIN_STKO_CHUNK_SIZE)
+                       StkOChunkSize = MIN_STKO_CHUNK_SIZE;
+               } else {
+                   fprintf(stderr, "setupRtsFlags: missing size for -o\n");
+                   error = 1;
+               }
+               break;
+
+# ifdef PAR
+             case 'O':
+               OutputDisabled = rtsTrue;
+               break;
+
+# else /* PAR */
+
+#  if !defined(GRAN)
+             case 'b': /* will fall through to disaster */
+#  else
+             case 'b':
+               if (rts_argv[arg][2] != '\0') {
+
+                 /* Should we emulate hbcpp */
+                 if(strcmp((rts_argv[arg]+2),"roken")==0) {
+                   ++DoAlwaysCreateThreads;
+                   strcpy(rts_argv[arg]+2,"oring");
+                 }
+
+                 /* or a ridiculously idealised simulator */
+                 if(strcmp((rts_argv[arg]+2),"oring")==0) {
+                   gran_latency = gran_fetchtime = gran_additional_latency =
+                     gran_gunblocktime = gran_lunblocktime
+                       = gran_threadcreatetime = gran_threadqueuetime
+                         = gran_threadscheduletime = gran_threaddescheduletime
+                           = gran_threadcontextswitchtime
+                             = 0;
+
+                   gran_mpacktime = gran_munpacktime = 0;
+
+                   gran_arith_cost = gran_float_cost = gran_load_cost
+                     = gran_store_cost = gran_branch_cost = 0;
+
+                   gran_heapalloc_cost = 1;
+
+                   /* ++DoFairSchedule; */
+                   ++DoStealThreadsFirst;
+                   ++DoThreadMigration;
+                   ++do_gr_profile;
+                 }
+
+                 /* or a ridiculously idealised simulator */
+                 if(strcmp((rts_argv[arg]+2),"onzo")==0) {
+                   gran_latency = gran_fetchtime = gran_additional_latency =
+                     gran_gunblocktime = gran_lunblocktime
+                       = gran_threadcreatetime = gran_threadqueuetime
+                         = gran_threadscheduletime = gran_threaddescheduletime
+                           = gran_threadcontextswitchtime
+                             = 0;
+
+                   gran_mpacktime = gran_munpacktime = 0;
+
+                   /* Keep default values for these
+                   gran_arith_cost = gran_float_cost = gran_load_cost
+                     = gran_store_cost = gran_branch_cost = 0;
+                     */
+
+                   gran_heapalloc_cost = 1;
+
+                   /* ++DoFairSchedule; */       /* -b-R */
+                   /* ++DoStealThreadsFirst; */  /* -b-T */
+                   ++DoReScheduleOnFetch;        /* -bZ */
+                   ++DoThreadMigration;          /* -bM */
+                   ++do_gr_profile;              /* -bP */
+#   if defined(GRAN_CHECK) && defined(GRAN)
+                   debug = 0x20;       /* print event statistics   */
+#   endif
+                 }
+
+                 /* Communication and task creation cost parameters */
+                 else switch(rts_argv[arg][2]) {
+                   case 'l':
+                     if (rts_argv[arg][3] != '\0')
+                       {
+                         gran_gunblocktime = gran_latency = decode(rts_argv[arg]+3);
+                         gran_fetchtime = 2* gran_latency;
+                       }
+                     else
+                       gran_latency = LATENCY;
+                     break;
+
+                   case 'a':
+                     if (rts_argv[arg][3] != '\0')
+                       gran_additional_latency = decode(rts_argv[arg]+3);
+                     else
+                       gran_additional_latency = ADDITIONAL_LATENCY;
+                     break;
+
+                   case 'm':
+                     if (rts_argv[arg][3] != '\0')
+                       gran_mpacktime = decode(rts_argv[arg]+3);
+                     else
+                       gran_mpacktime = MSGPACKTIME;
+                     break;
+
+                   case 'x':
+                     if (rts_argv[arg][3] != '\0')
+                       gran_mtidytime = decode(rts_argv[arg]+3);
+                     else
+                       gran_mtidytime = 0;
+                     break;
+
+                   case 'r':
+                     if (rts_argv[arg][3] != '\0')
+                       gran_munpacktime = decode(rts_argv[arg]+3);
+                     else
+                       gran_munpacktime = MSGUNPACKTIME;
+                     break;
+
+                   case 'f':
+                     if (rts_argv[arg][3] != '\0')
+                       gran_fetchtime = decode(rts_argv[arg]+3);
+                     else
+                       gran_fetchtime = FETCHTIME;
+                     break;
+
+                   case 'n':
+                     if (rts_argv[arg][3] != '\0')
+                       gran_gunblocktime = decode(rts_argv[arg]+3);
+                     else
+                       gran_gunblocktime = GLOBALUNBLOCKTIME;
+                     break;
+
+                   case 'u':
+                     if (rts_argv[arg][3] != '\0')
+                       gran_lunblocktime = decode(rts_argv[arg]+3);
+                     else
+                       gran_lunblocktime = LOCALUNBLOCKTIME;
+                     break;
+
+                   /* Thread-related metrics */
+                   case 't':
+                     if (rts_argv[arg][3] != '\0')
+                       gran_threadcreatetime = decode(rts_argv[arg]+3);
+                     else
+                       gran_threadcreatetime = THREADCREATETIME;
+                     break;
+
+                   case 'q':
+                     if (rts_argv[arg][3] != '\0')
+                       gran_threadqueuetime = decode(rts_argv[arg]+3);
+                     else
+                       gran_threadqueuetime = THREADQUEUETIME;
+                     break;
+
+                   case 'c':
+                     if (rts_argv[arg][3] != '\0')
+                       gran_threadscheduletime = decode(rts_argv[arg]+3);
+                     else
+                       gran_threadscheduletime = THREADSCHEDULETIME;
+
+                     gran_threadcontextswitchtime = gran_threadscheduletime
+                                                  + gran_threaddescheduletime;
+                     break;
+
+                   case 'd':
+                     if (rts_argv[arg][3] != '\0')
+                       gran_threaddescheduletime = decode(rts_argv[arg]+3);
+                     else
+                       gran_threaddescheduletime = THREADDESCHEDULETIME;
+
+                     gran_threadcontextswitchtime = gran_threadscheduletime
+                                                  + gran_threaddescheduletime;
+                     break;
+
+                   /* Instruction Cost Metrics */
+                   case 'A':
+                     if (rts_argv[arg][3] != '\0')
+                       gran_arith_cost = decode(rts_argv[arg]+3);
+                     else
+                       gran_arith_cost = ARITH_COST;
+                     break;
+
+                   case 'F':
+                     if (rts_argv[arg][3] != '\0')
+                       gran_float_cost = decode(rts_argv[arg]+3);
+                     else
+                       gran_float_cost = FLOAT_COST;
+                     break;
+                     
+                   case 'B':
+                     if (rts_argv[arg][3] != '\0')
+                       gran_branch_cost = decode(rts_argv[arg]+3);
+                     else
+                       gran_branch_cost = BRANCH_COST;
+                     break;
+
+                   case 'L':
+                     if (rts_argv[arg][3] != '\0')
+                       gran_load_cost = decode(rts_argv[arg]+3);
+                     else
+                       gran_load_cost = LOAD_COST;
+                     break;
+
+                   case 'S':
+                     if (rts_argv[arg][3] != '\0')
+                       gran_store_cost = decode(rts_argv[arg]+3);
+                     else
+                       gran_store_cost = STORE_COST;
+                     break;
+
+                   case 'H':
+                     if (rts_argv[arg][3] != '\0')
+                       gran_heapalloc_cost = decode(rts_argv[arg]+3);
+                     else
+                       gran_heapalloc_cost = 0;
+                     break;
+
+                   case 'y':
+                     if (rts_argv[arg][3] != '\0')
+                       FetchStrategy = decode(rts_argv[arg]+3);
+                     else
+                       FetchStrategy = 4; /* default: fetch everything */
+                     break;
+
+                   /* General Parameters */
+                   case 'p':
+                     if (rts_argv[arg][3] != '\0')
+                       {
+                         max_proc = decode(rts_argv[arg]+3);
+                         if(max_proc > MAX_PROC || max_proc < 1)
+                           {
+                             fprintf(stderr,"setupRtsFlags: no more than %u processors allowed\n", MAX_PROC);
+                             error = 1;
+                           }
+                       }
+                     else
+                       max_proc = MAX_PROC;
+                     break;
+
+                   case 'C':
+                     ++DoAlwaysCreateThreads;
+                     ++DoThreadMigration;
+                     break;
+
+                   case 'G':
+                     ++DoGUMMFetching;
+                     break;
+
+                   case 'M':
+                     ++DoThreadMigration;
+                     break;
+
+                   case 'R':
+                     ++DoFairSchedule;
+                     break;
+
+                   case 'T':
+                     ++DoStealThreadsFirst;
+                     ++DoThreadMigration;
+                     break;
+
+                   case 'Z':
+                     ++DoReScheduleOnFetch;
+                     break;
+
+                   case 'z':
+                     ++SimplifiedFetch;
+                     break;
+
+                   case 'N':
+                     ++PreferSparksOfLocalNodes;
+                     break;
+
+                   case 'b':
+                     ++do_gr_binary;
+                     break;
+
+                   case 'P':
+                     ++do_gr_profile;
+                     break;
+
+                   case 's':
+                     ++do_sp_profile;
+                     break;
+
+                   case '-':
+                     switch(rts_argv[arg][3]) {
+
+                      case 'C':
+                        DoAlwaysCreateThreads=0;
+                        DoThreadMigration=0;
+                        break;
+
+                      case 'G':
+                        DoGUMMFetching=0;
+                        break;
+
+                      case 'M':
+                        DoThreadMigration=0;
+                        break;
+
+                       case 'R':
+                        DoFairSchedule=0;
+                        break;
+
+                      case 'T':
+                        DoStealThreadsFirst=0;
+                        DoThreadMigration=0;
+                        break;
+
+                      case 'Z':
+                        DoReScheduleOnFetch=0;
+                        break;
+
+                      case 'N':
+                        PreferSparksOfLocalNodes=0;
+                        break;
+
+                      case 'P':
+                        do_gr_profile=0;
+                        no_gr_profile=1;
+                        break;
+
+                      case 's':
+                        do_sp_profile=0;
+                        break;
+
+                      case 'b':
+                        do_gr_binary=0;
+                        break;
+
+                      default:
+                        badoption( rts_argv[arg] );
+                        break;
+                      }
+                     break;
+
+#   if defined(GRAN_CHECK) && defined(GRAN)
+                   case 'D':
+                     switch(rts_argv[arg][3]) {
+                         case 'e':       /* event trace */
+                           fprintf(stderr,"Printing event trace.\n");
+                           ++event_trace;
+                           break;
+
+                         case 'f':
+                           fprintf(stderr,"Printing forwarding of FETCHNODES.\n");
+                           debug |= 0x2; /* print fwd messages */
+                           break;
+
+                         case 'z':
+                           fprintf(stderr,"Check for blocked on fetch.\n");
+                           debug |= 0x4; /* debug non-reschedule-on-fetch */
+                           break;
+
+                         case 't':
+                           fprintf(stderr,"Check for TSO asleep on fetch.\n");
+                           debug |= 0x10; /* debug TSO asleep for fetch  */
+                           break;
+
+                         case 'E':
+                           fprintf(stderr,"Printing event statistics.\n");
+                           debug |= 0x20; /* print event statistics   */
+                           break;
+
+                         case 'F':
+                           fprintf(stderr,"Prohibiting forward.\n");
+                           NoForward = 1; /* prohibit forwarding   */
+                           break;
+
+                         case 'm':
+                           fprintf(stderr,"Printing fetch misses.\n");
+                           PrintFetchMisses = 1; /* prohibit forwarding   */
+                           break;
+
+                         case 'd':
+                           fprintf(stderr,"Debug mode.\n");
+                           debug |= 0x40; 
+                           break;
+
+                         case 'D':
+                           fprintf(stderr,"Severe debug mode.\n");
+                           debug |= 0x80; 
+                           break;
+
+                         case '\0':
+                           debug = 1;
+                           break;
+
+                         default:
+                           badoption( rts_argv[arg] );
+                           break;
+                         }
+                     break;
+#   endif
+                   default:
+                     badoption( rts_argv[arg] );
+                     break;
+                   }
+               }
+               do_gr_sim++;
+               contextSwitchTime = 0;
+               break;
+#  endif
+             case 'J':
+             case 'Q':
+             case 'D':
+             case 'R':
+             case 'L':
+             case 'O':
+               fprintf(stderr, "setupRtsFlags: Not built for parallel execution: %s\n", rts_argv[arg]);
+               error = 1;
+# endif        /* PAR */
+#else  /* CONCURRENT */
+             case 't':
+               fprintf(stderr, "setupRtsFlags: Not built for threaded execution: %s\n", rts_argv[arg]);
+               error = 1;
+
+#endif /* CONCURRENT */
+             case 'H': /* SM options -- ignore */
+             case 'A':
+             case 'G':
+             case 'F':
+             case 'K':
+             case 'M':
+             case 'B':
+             case 'T':
+#ifdef GCdu
+             case 'u': /* set dual mode threshold */
+#endif
+               break;
+
+             default: /* Unknown option ! */
+               fprintf(stderr, "setupRtsFlags: Unknown RTS option: %s\n", rts_argv[arg]);
+               error = 1;
+               break;
+           }
+         }
+       else {
+           fflush(stdout);
+           fprintf(stderr, "setupRtsFlags: Unexpected RTS argument: %s\n",
+                   rts_argv[arg]);
+           error = 1;
+       }
+    }
+    if (error == 1) {
+       char  **p;
+        fflush(stdout);
+       for (p = flagtext; *p; p++)
+           fprintf(stderr, "%s\n", *p);
+       EXIT(EXIT_FAILURE);
+    }
+}
+\end{code}
+
+Sets up and returns a string indicating the date/time of the run.
+Successive calls simply return the same string again. Initially
+called by @main.lc@ to initialise the string at the start of the run.
+Only used for profiling.
+
+\begin{code}
+#if defined(USE_COST_CENTRES) || defined(CONCURRENT)
+# include <time.h>
+
+char *
+time_str(STG_NO_ARGS)
+{
+    static time_t now = 0;
+    static char nowstr[26];
+
+    if (now == 0) {
+       time(&now);
+       strcpy(nowstr, ctime(&now));
+       strcpy(nowstr+16,nowstr+19);
+       nowstr[21] = '\0';
+    }
+    return nowstr;
+}
+#endif /* profiling */
+\end{code}
+
+ToDo: Will this work under threads?
+
+\begin{code}
+StgStablePtr errorHandler = -1;
+
+StgInt getErrorHandler()
+{
+  return (StgInt) errorHandler;
+}
+
+#ifndef PAR
+
+void raiseError( handler )
+StgStablePtr handler;
+{
+  if (handler == -1) {
+    shutdownHaskell();
+  } else {
+    TopClosure = deRefStablePointer( handler );
+    longjmp(restart_main,1);
+  }
+}
+\end{code}
+
+\begin{code}
+StgInt
+catchError( newErrorHandler )
+StgStablePtr newErrorHandler;
+{
+  StgStablePtr oldErrorHandler = errorHandler;
+  errorHandler = newErrorHandler;
+  return oldErrorHandler;
+}
+
+#endif
+\end{code}
+
+If we have installed an error handler, we might want to
+indicate that we have successfully recovered from an error by
+decrementing the counter.
+
+\begin{code}
+void
+decrementErrorCount()
+{
+  ErrorIO_call_count-=1;       
+}
+
+\end{code}