summaryrefslogtreecommitdiff
path: root/ghc/includes/CostCentre.lh
diff options
context:
space:
mode:
Diffstat (limited to 'ghc/includes/CostCentre.lh')
-rw-r--r--ghc/includes/CostCentre.lh674
1 files changed, 674 insertions, 0 deletions
diff --git a/ghc/includes/CostCentre.lh b/ghc/includes/CostCentre.lh
new file mode 100644
index 0000000000..05297e2326
--- /dev/null
+++ b/ghc/includes/CostCentre.lh
@@ -0,0 +1,674 @@
+%************************************************************************
+%* *
+\section[CostCentre.lh]{Definitions for Cost Centre Profiling}
+%* *
+%************************************************************************
+
+Multi-slurp protection:
+\begin{code}
+#ifndef CostCentre_H
+#define CostCentre_H
+\end{code}
+
+For threaded activity profiling, we need a few bits of the CostCentre
+environment to be defined, despite the fact that we don't have CostCentre
+fields in closures.
+
+\begin{code}
+#if defined(USE_COST_CENTRES) || defined(CONCURRENT)
+
+# define CC_EXTERN(cc_ident) \
+ extern struct cc CAT2(cc_ident,_struct); \
+ extern CostCentre cc_ident
+
+extern CostCentre CCC; /* the current cost centre */
+
+extern CostCentre Registered_CC;/* registered cost centre list */
+
+CC_EXTERN(CC_MAIN); /* initial MAIN cost centre */
+CC_EXTERN(CC_GC); /* Garbage Collection cost center */
+
+# ifdef GUM
+CC_EXTERN(CC_MSG); /* Communications cost center */
+CC_EXTERN(CC_IDLE); /* Idle-time cost centre */
+# endif
+
+# define REGISTERED_END (CostCentre)4 /* end of list */
+ /* That 4 look likes a HACK, Patrick.
+ (WDP 94/06) */
+# define NOT_REGISTERED (CostCentre)0 /* not yet registered */
+
+\end{code}
+
+The compiler declares a static block for each @_scc_@ annotation in the
+source using the @CC_DECLARE@ macro where @label@, @module@ and
+@group@ are strings and @ident@ the cost centre identifier.
+
+\begin{code}
+# define CC_IS_CAF 'C'
+# define CC_IS_DICT 'D'
+# define CC_IS_SUBSUMED 'S'
+# define CC_IS_BORING '\0'
+
+# define STATIC_CC_REF(cc_ident) &CAT2(cc_ident,_struct)
+# define DYN_CC_REF(cc_ident) cc_ident /* unused */
+
+# define CC_DECLARE(cc_ident,name,module,group,subsumed,is_local) \
+ is_local struct cc CAT2(cc_ident,_struct) \
+ = {NOT_REGISTERED, UNHASHED, name, module, group, \
+ subsumed, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; \
+ is_local CostCentre cc_ident = STATIC_CC_REF(cc_ident)
+
+#endif /* defined(USE_COST_CENTRES) || defined(CONCURRENT) */
+\end{code}
+
+Definitions relating to the profiling field as a whole.
+
+\begin{code}
+#define PROF_FIXED_HDR (CC_HDR_SIZE)
+#define PROF_HDR_POSN AFTER_PAR_HDR
+#define AFTER_PROF_HDR (PROF_FIXED_HDR+PROF_HDR_POSN)
+#define SET_PROF_HDR(closure,cc) SET_CC_HDR(closure,cc)
+#define SET_STATIC_PROF_HDR(ident) SET_STATIC_CC_HDR(ident)
+\end{code}
+
+%************************************************************************
+%* *
+\subsection[no-cost-centres]{Dummy definitions if no cost centres}
+%* *
+%************************************************************************
+
+The cost-centre profiling is only on if the driver turns on
+@USE_COST_CENTRES@.
+
+These are the {\em dummy} definitions in force if we do {\em NOT}
+turn on @USE_COST_CENTRES@. Get them out of the way....
+
+\begin{code}
+#if !defined(USE_COST_CENTRES)
+
+/*** Declaration Definitions ***/
+
+# define CAT_DECLARE(base_name, kind, descr, type)
+
+# define CC_HDR_SIZE 0 /* No CC in fixed header */
+
+# define SET_CC_HDR(closure, cc) /* Dont set CC header */
+# define SET_STATIC_CC_HDR(cc) /* No static CC header */
+
+# define SET_CCC(cc_ident,is_dupd)
+
+# define RESTORE_CCC(cc)
+
+# define ENTER_CC_T(cc)
+# define ENTER_CC_TCL(closure)
+# define ENTER_CC_F(cc)
+# define ENTER_CC_FCL(closure)
+# define ENTER_CC_PAP(cc)
+# define ENTER_CC_PAP_CL(closure)
+
+/*** Timer and Heap Definitions ***/
+
+# define OR_INTERVAL_EXPIRED /* No || as it is false */
+
+# define CC_ALLOC(cc, size, kind)
+# define HEAP_PROFILE_CLOSURE(closure,size)
+
+# ifndef GUM
+# define START_TIME_PROFILER
+# define RESTART_TIME_PROFILER
+# define STOP_TIME_PROFILER
+# endif
+
+#endif /* !defined(USE_COST_CENTRES) */
+\end{code}
+
+%************************************************************************
+%* *
+\subsection[declaring-cost-centres]{Declaring Cost Centres}
+%* *
+%************************************************************************
+
+Now for the cost-centre profiling stuff.
+
+%************************************************************************
+%* *
+\subsection[cost-centres]{Location of Cost Centres}
+%* *
+%************************************************************************
+
+We have a current cost centre, a list of registered cost centres, and
+an additional cost centre field within the fixed header of all
+closures. This is adjacent to the info pointer.
+
+\begin{code}
+#if defined(USE_COST_CENTRES)
+
+CC_EXTERN(CC_SUBSUMED); /* top level fns SUBSUMED cost centre */
+CC_EXTERN(CC_OVERHEAD); /* costs due only to profiling machinery */
+CC_EXTERN(CC_DONTZuCARE); /* placeholder only */
+
+CC_EXTERN(CC_CAFs); /* prelude cost centre (CAFs only) */
+CC_EXTERN(CC_DICTs); /* prelude cost centre (DICTs only) */
+
+# define IS_CAF_OR_DICT_CC(cc) \
+ (((cc)->is_subsumed == CC_IS_CAF) || ((cc)->is_subsumed == CC_IS_DICT))
+
+# define IS_SUBSUMED_CC(cc) ((cc)->is_subsumed == CC_IS_SUBSUMED)
+
+\end{code}
+
+Definitions referring to the Cost Centre sub-field of the fixed header.
+\begin{code}
+
+# define CC_HDR_SIZE 1 /* words of header */
+ /*R SMinterface.lh */
+
+# define CC_HDR_POSN PROF_HDR_POSN /* position in header */
+
+# define CC_HDR(closure) (((P_)(closure))[CC_HDR_POSN])
+
+# define SET_CC_HDR(closure, cc) \
+ CC_HDR(closure) = (W_)(cc) /* Set closures cost centre */
+ /*R SMinterface.lh (CCC) */
+\end{code}
+
+For static closures ...
+\begin{code}
+# define SET_STATIC_CC_HDR(cc_ident) \
+ , (W_) STATIC_CC_REF(cc_ident) /* static initialisation */
+ /*R SMinterface.lh */
+\end{code}
+
+The @/*R @{\em file}@ */@ comments indicate that the macro is used
+regardless in {\em file} so we need a null definition if cost centres
+are not being used.
+
+%************************************************************************
+%* *
+\subsection{Setting the Current Cost Centre}
+%* *
+%************************************************************************
+
+On execution of an @_scc_@ expression a new cost centre is set.
+
+If the new cost centre is different from the CCC we set the CCC and
+count the entry.
+
+If the cost centre is the same as the CCC no action is required. We do
+not count the entry to avoid large counts arising from simple
+recursion. (Huh? WDP 94/07)
+
+\begin{code}
+# define SET_CCC_X(cc,is_dupd) \
+ do { \
+ if (!(is_dupd)) { CCC->sub_scc_count++; } /* inc subcc count of CCC */ \
+ CCC = (CostCentre)(cc); /* set CCC to ident cc */ \
+ if (!(is_dupd)) { CCC->scc_count++; } /* inc scc count of new CCC*/ \
+ } while(0)
+
+# define SET_CCC(cc_ident,is_dupd) SET_CCC_X(STATIC_CC_REF(cc_ident),is_dupd)
+\end{code}
+
+We have this @RESTORE_CCC@ macro, rather than just an assignment,
+in case we want to do any paranoia-checking here.
+\begin{code}
+# define RESTORE_CCC(cc) \
+ do { \
+ CCC = (CostCentre) (cc); \
+ ASSERT_IS_REGISTERED(CCC,1); \
+ } while(0)
+\end{code}
+
+On entry to top level CAFs we count the scc ...
+\begin{code}
+# define ENTER_CC_CAF_X(cc) \
+ do { \
+ CCC->sub_cafcc_count++; /* inc subcaf count of CCC */ \
+ CCC = (CostCentre)(cc); /* set CCC to ident cc */ \
+ ASSERT_IS_REGISTERED(CCC,1); \
+ CCC->cafcc_count++; /* inc cafcc count of CCC */ \
+ } while(0)
+
+# define ENTER_CC_CAF(cc_ident) ENTER_CC_CAF_X(STATIC_CC_REF(cc_ident))
+# define ENTER_CC_CAF_CL(closure) ENTER_CC_CAF_X((CostCentre)CC_HDR(closure))
+\end{code}
+
+On entering a closure we only count the enter to thunks ...
+\begin{code}
+# define ENTER_CC_T(cc) \
+ do { \
+ CCC = (CostCentre)(cc); \
+ ASSERT_IS_REGISTERED(CCC,1); \
+ CCC->thunk_count++; /* inc thunk count of new CCC */ \
+ } while(0)
+
+# define ENTER_CC_TCL(closure) \
+ ENTER_CC_T(CC_HDR(closure))
+
+/* Here is our special "hybrid" case when we do *not* set the CCC.
+ (a) The closure is a function, not a thunk;
+ (b) The CC is CAF/DICT-ish.
+*/
+# define ENTER_CC_F(centre) \
+ do { \
+ CostCentre cc = (CostCentre) (centre); \
+ ASSERT_IS_REGISTERED(cc,1); \
+ if ( ! IS_CAF_OR_DICT_CC(cc) ) { \
+ CCC = cc; \
+ } \
+ CCC->function_count++; \
+ } while(0)
+
+# define ENTER_CC_FCL(closure) \
+ ENTER_CC_F(CC_HDR(closure))
+
+/* These ENTER_CC_PAP things are only used in the RTS */
+
+# define ENTER_CC_PAP(centre) \
+ do { \
+ CostCentre cc = (CostCentre) (centre); \
+ ASSERT_IS_REGISTERED(cc,1); \
+ if ( ! IS_CAF_OR_DICT_CC(cc) ) { \
+ CCC = cc; \
+ } \
+ CCC->pap_count++; \
+ } while(0)
+
+# define ENTER_CC_PAP_CL(closure) \
+ ENTER_CC_PAP(CC_HDR(closure))
+
+#endif /* USE_COST_CENTRES */
+\end{code}
+
+%************************************************************************
+%* *
+\subsection{``Registering'' cost-centres}
+%* *
+%************************************************************************
+
+Cost centres are registered at startup by calling a registering
+routine in each module. Each module registers its cost centres and
+calls the registering routine for all imported modules. The RTS calls
+the registering routine for the module Main. This registering must be
+done before initialisation since the evaluation required for
+initialisation may use the cost centres.
+
+As the code for each module uses tail calls we use an auxiliary stack
+(in the heap) to record imported modules still to be registered. At
+the bottom of the stack is NULL which indicates that
+@miniInterpretEnd@ should be resumed.
+
+@START_REGISTER@ and @END_REGISTER@ are special macros used to
+delimit the function. @END_REGISTER@ pops the next registering
+routine off the stack and jumps to it. @REGISTER_CC@ registers a cost
+centre. @REGISTER_IMPORT@ pushes a modules registering routine onto
+the register stack.
+
+\begin{code}
+#if defined(USE_COST_CENTRES)
+
+extern F_ _regMain (STG_NO_ARGS);
+extern F_ *register_stack;
+
+# define PUSH_REGISTER_STACK(reg_function) \
+ *(register_stack++) = (F_)reg_function
+
+# define POP_REGISTER_STACK \
+ *(--register_stack)
+
+extern I_ SM_trace;
+
+# define START_REGISTER_CCS(reg_mod_name) \
+ static int _module_registered = 0; \
+ STGFUN(reg_mod_name) { \
+ FUNBEGIN; \
+ if (! _module_registered) { \
+ _module_registered = 1
+
+# define START_REGISTER_PRELUDE(reg_mod_name) \
+ static int CAT2(reg_mod_name,_done) = 0; \
+ STGFUN(reg_mod_name) { \
+ FUNBEGIN; \
+ if (! CAT2(reg_mod_name,_done)) { \
+ CAT2(reg_mod_name,_done) = 1
+
+# define REGISTER_IMPORT(reg_mod_name) \
+ do { extern F_ reg_mod_name (STG_NO_ARGS) ; \
+ PUSH_REGISTER_STACK(reg_mod_name) ; \
+ } while (0)
+
+# define END_REGISTER_CCS() \
+ }; \
+ do { \
+ F_ cont = POP_REGISTER_STACK; \
+ if (cont == NULL) { \
+ RESUME_(miniInterpretEnd); \
+ } else { \
+ JMP_(cont); \
+ } \
+ } while(0); \
+ FUNEND; }
+
+#endif /* USE_COST_CENTRES */
+\end{code}
+
+We don't want to attribute costs to an unregistered cost-centre:
+\begin{code}
+#if !defined(USE_COST_CENTRES) || !defined(DEBUG)
+# define ASSERT_IS_REGISTERED(cc,chk_not_overhead) /*nothing*/
+#else
+# define ASSERT_IS_REGISTERED(cc,chk_not_overhead) \
+ do { \
+ CostCentre c_c = (CostCentre) (cc); \
+ if (c_c->registered == NOT_REGISTERED) { \
+ fprintf(stderr,"Entering unregistered CC: %s\n",c_c->label); \
+ /* unsafe c-call, BTW */ \
+ } \
+ if ( (chk_not_overhead) && c_c == STATIC_CC_REF(CC_OVERHEAD) ) { \
+ fprintf(stderr,"CC should not be OVERHEAD!: %s\n",c_c->label); \
+ /* unsafe c-call, BTW */ \
+ } } while(0)
+#endif
+
+#define REGISTER_CC(cc) \
+ do { \
+ extern CostCentre cc; \
+ if (((CostCentre)(cc))->registered == NOT_REGISTERED) { \
+ ((CostCentre)(cc))->registered = Registered_CC; \
+ Registered_CC = (CostCentre)(cc); \
+ }} while(0)
+
+\end{code}
+
+%************************************************************************
+%* *
+\subsection[declaring-closure-categories]{Declaring Closure Categories}
+%* *
+%************************************************************************
+
+Closure category records are attached to the info table of the
+closure. They are declared with the info table. Hashing will map
+similar categories to the same hash value allowing statistics to be
+grouped by closure category.
+
+The declaration macros expand to nothing if cost centre profiling is
+not required.
+
+Note from ADR: Very dubious Malloc Ptr addition -- should probably just
+reuse @CON_K@ (or something) in runtime/main/StgStartup.lhc.
+Similarily, the SP stuff should probably be the highly uninformative
+@INTERNAL_KIND@.
+
+\begin{code}
+#if defined(USE_COST_CENTRES)
+
+# define CON_K 1
+# define FN_K 2
+# define PAP_K 3
+# define THK_K 4
+# define BH_K 5
+# define ARR_K 6
+
+# ifndef PAR
+# define MallocPtr_K 7 /* Malloc Pointer */
+# define SPT_K 8 /* Stable Pointer Table */
+# endif /* !PAR */
+
+# define INTERNAL_KIND 10
+
+typedef struct ClCat {
+ hash_t index_val; /* hashed value */
+ I_ selected; /* is this category selected (-1 == not memoised, selected? 0 or 1) */
+ I_ kind; /* closure kind -- as above */
+ char *descr; /* source derived string detailing closure description */
+ char *type; /* source derived string detailing closure type */
+} *ClCategory;
+
+/* We put pointers to these ClCat things in info tables.
+ We need these ClCat things because they are mutable,
+ whereas info tables are immutable. (WDP 94/11)
+
+ We really should not make funny names by appending _CAT.
+*/
+
+# define MK_CAT_IDENT(i) CAT2(i,_CAT)
+# define REF_CAT_IDENT(i) (&MK_CAT_IDENT(i))
+
+# define CAT_DECLARE(base_name, kind, descr, type) \
+ static struct ClCat MK_CAT_IDENT(base_name) = {UNHASHED,-1,kind,descr,type};
+
+#endif /* USE_COST_CENTRES */
+\end{code}
+
+%************************************************************************
+%* *
+\subsection[timer-interupts]{Processing of Timer Signals}
+%* *
+%************************************************************************
+
+Stuff to do with timer signals:
+\begin{code}
+#if defined(USE_COST_CENTRES) || defined(GUM)
+
+extern I_ time_profiling; /* Flag indicating if timer/serial profiling is required */
+
+extern I_ interval_expired; /* Flag set by signal handler */
+extern I_ current_interval; /* Current interval number -- used as time stamp */
+extern I_ interval_ticks; /* No of ticks in an interval */
+
+extern I_ previous_ticks; /* ticks in previous intervals */
+extern I_ current_ticks; /* ticks in current interval */
+
+extern void set_time_profile_handler(STG_NO_ARGS);
+extern void start_time_profiler(STG_NO_ARGS);
+extern void restart_time_profiler(STG_NO_ARGS);
+extern void stop_time_profiler(STG_NO_ARGS);
+
+# define TICK_FREQUENCY 50 /* ticks per second */
+# define TICK_MILLISECS (1000/TICK_FREQUENCY) /* milli-seconds per tick */
+
+# ifdef CONCURRENT
+extern I_ profilerTicks;
+extern I_ tick_millisecs;
+# endif
+
+# define DEFAULT_INTERVAL TICK_FREQUENCY /* 1 second */
+
+/* These are never called directly from threaded code */
+# define START_TIME_PROFILER ULTRASAFESTGCALL0(void,(void *),start_time_profiler) /*R StgOverflow.lc */
+# define RESTART_TIME_PROFILER ULTRASAFESTGCALL0(void,(void *),restart_time_profiler) /*R StgOverflow.lc */
+# define STOP_TIME_PROFILER ULTRASAFESTGCALL0(void,(void *),stop_time_profiler) /*R StgOverflow.lc */
+
+# if defined(USE_COST_CENTRES)
+# define OR_INTERVAL_EXPIRED || (interval_expired) /*R StgMacros.h */
+# endif
+\end{code}
+
+
+%************************************************************************
+%* *
+\subsection[indexing]{Indexing of Cost Centres and Categories}
+%* *
+%************************************************************************
+
+Cost Centres and Closure Categories are hashed to provide indexes
+against which arbitrary information can be stored. These indexes are
+memoised in the appropriate cost centre or category record and
+subsequent hashes avoided by the index routine (it simply returns the
+memoised index).
+
+There are different features which can be hashed allowing information
+to be stored for different groupings. Cost centres have the cost
+centre recorded (using the pointer), module and group. Closure
+categories have the closure description and the type
+description. Records with the same feature will be hashed to the same
+index value.
+
+The initialisation routines, @init_index_<feature>@, allocate a hash
+table in which the cost centre / category records are stored. The
+lower bound for the table size is taken from @max_<feature>_no@. They
+return the actual table size used (the next power of 2). Unused
+locations in the hash table are indicated by a 0 entry. Successive
+@init_index_<feature>@ calls just return the actual table size.
+
+Calls to @index_<feature>@ will insert the cost centre / category
+record in the <feature> hash table, if not already inserted. The hash
+index is memoised in the record and returned.
+
+CURRENTLY ONLY ONE MEMOISATION SLOT IS AVILABLE IN EACH RECORD SO
+HASHING CAN ONLY BE DONE ON ONE FEATURE FOR EACH RECORD. This can be
+easily relaxed at the expense of extra memoisation space or continued
+rehashing.
+
+The initialisation routines must be called before initialisation of
+the stacks and heap as they require to allocate storage. It is also
+expected that the caller may want to allocate additional storage in
+which to store profiling information based on the return table size
+value(s).
+
+\begin{code}
+# if defined(USE_COST_CENTRES)
+
+# define DEFAULT_MAX_CC 4096
+# define DEFAULT_MAX_MOD 256
+# define DEFAULT_MAX_GRP 128
+# define DEFAULT_MAX_DESCR 4096
+# define DEFAULT_MAX_TYPE 1024
+
+extern hash_t max_cc_no; /* Hash on CC ptr */
+extern CostCentre *index_cc_table;
+extern hash_t init_index_cc(STG_NO_ARGS);
+extern hash_t index_cc PROTO((CostCentre cc));
+
+extern hash_t max_mod_no; /* Hash on CC module */
+extern CostCentre *index_mod_table;
+extern hash_t init_index_mod(STG_NO_ARGS);
+extern hash_t index_mod PROTO((CostCentre cc));
+
+extern hash_t max_grp_no; /* Hash on CC group */
+extern CostCentre *index_grp_table;
+extern hash_t init_index_grp(STG_NO_ARGS);
+extern hash_t index_grp PROTO((CostCentre cc));
+
+extern hash_t max_descr_no; /* Hash on closure description */
+extern ClCategory *index_descr_table;
+extern hash_t init_index_descr(STG_NO_ARGS);
+extern hash_t index_descr PROTO((ClCategory clcat));
+
+extern hash_t max_type_no; /* Hash on type description */
+extern ClCategory *index_type_table;
+extern hash_t init_index_type(STG_NO_ARGS);
+extern hash_t index_type PROTO((ClCategory clcat));
+
+# endif /* USE_COST_CENTRES */
+\end{code}
+
+
+%************************************************************************
+%* *
+\subsection[metering]{Metering of Statistics}
+%* *
+%************************************************************************
+
+@scc_count@ is incremented by the @SetCC@ macro in section
+\ref{manipulating-cost-centres} above. Below we have the time tick and
+memory alloc macros.
+
+\begin{code}
+# define CC_TICK(cc) \
+ do { CostCentre centre = (CostCentre) (cc); \
+ ASSERT_IS_REGISTERED(centre,1); \
+ centre->time_ticks += 1; \
+ } while(0)
+
+# if defined(USE_COST_CENTRES)
+# define CC_ALLOC(cc, size, kind) \
+ do { CostCentre cc_ = (CostCentre) (cc); \
+ ASSERT_IS_REGISTERED(cc_,0/*OK if OVERHEAD*/); \
+ cc_->mem_allocs += 1; \
+ cc_->mem_alloc += (size) - (PROF_FIXED_HDR + AGE_FIXED_HDR); \
+ } while(0) /* beware name-capture by ASSERT_IS...! */
+# endif
+\end{code}
+
+
+%************************************************************************
+%* *
+\subsection[cost-centre-profiling]{Cost Centre Profiling}
+%* *
+%************************************************************************
+
+\begin{code}
+extern I_ cc_profiling; /* Are we performing/reporting cc profiling? */
+extern I_ heap_profiling_reqd; /* Are we performing heap profiling? */
+
+# define SORTCC_LABEL 'C'
+# define SORTCC_TIME 'T'
+# define SORTCC_ALLOC 'A'
+extern char cc_profiling_sort; /* How to sort cost centre report */
+
+extern I_ init_cc_profiling PROTO((I_ rts_argc, char *rts_argv[], char *prog_argv[]));
+extern void report_cc_profiling PROTO((I_ final));
+
+extern void cc_register(STG_NO_ARGS);
+extern void cc_sort PROTO((CostCentre *sort, char sort_on));
+\end{code}
+
+%************************************************************************
+%* *
+\subsection[heap-profiling]{Heap Profiling}
+%* *
+%************************************************************************
+
+\begin{code}
+# define HEAP_NO_PROFILING 0 /* N.B. Used as indexes into arrays */
+
+# if defined(USE_COST_CENTRES)
+
+# define HEAP_BY_CC 1
+# define HEAP_BY_MOD 2
+# define HEAP_BY_GRP 3
+# define HEAP_BY_DESCR 4
+# define HEAP_BY_TYPE 5
+# define HEAP_BY_TIME 6
+
+# define CCchar 'C'
+# define MODchar 'M'
+# define GRPchar 'G'
+# define DESCRchar 'D'
+# define TYPEchar 'Y'
+# define TIMEchar 'T'
+
+extern I_ heap_profile_init PROTO((I_ prof,
+ char *select_cc_str,
+ char *select_mod_str,
+ char *select_grp_str,
+ char *select_descr_str,
+ char *select_typ_str,
+ char *select_kind_str,
+ I_ select_age,
+ char *argv[]));
+
+extern void heap_profile_finish(STG_NO_ARGS);
+
+extern void heap_profile_setup(STG_NO_ARGS); /* called at start of heap profile */
+extern void heap_profile_done(STG_NO_ARGS); /* called at end of heap profile */
+
+extern void (* heap_profile_fn) PROTO((P_ closure,I_ size));
+
+extern I_ earlier_ticks; /* no. of earlier ticks grouped */
+extern hash_t time_intervals; /* no. of time intervals reported -- 18 */
+
+# define HEAP_PROFILE_CLOSURE(closure,size) \
+ STGCALL2(void,(void *, P_, I_),(*heap_profile_fn),closure,size) /*R SM2s.lh */
+
+# endif /* USE_COST_CENTRES */
+\end{code}
+
+End multi-slurp protection:
+\begin{code}
+#endif /* USE_COST_CENTRES || GUM */
+
+#endif /* CostCentre_H */
+\end{code}