/* -----------------------------------------------------------------------------
 *
 * (c) The GHC Team, 2009-2012
 *
 * Macros for profiling operations in STG code
 *
 * Do not #include this file directly: #include "Rts.h" instead.
 *
 * To understand the structure of the RTS headers, see the wiki:
 *   https://gitlab.haskell.org/ghc/ghc/wikis/commentary/source-tree/includes
 *
 * ---------------------------------------------------------------------------*/

#pragma once

/* -----------------------------------------------------------------------------
 * Data Structures
 * ---------------------------------------------------------------------------*/
/*
 * Note [struct alignment]
 * NB. be careful to avoid unwanted padding between fields, by
 * putting the 8-byte fields on an 8-byte boundary.  Padding can
 * vary between C compilers, and we don't take into account any
 * possible padding when generating CCS and CC decls in the code
 * generator (GHC.StgToCmm.Prof).
 */

typedef struct CostCentre_ {
    StgInt ccID;              // Unique Id, allocated by the RTS

    char * label;
    char * module;
    char * srcloc;

    // used for accumulating costs at the end of the run...
    StgWord64 mem_alloc;      // align 8 (Note [struct alignment])
    StgWord   time_ticks;

    StgBool is_caf;           // true <=> CAF cost centre

    struct CostCentre_ *link;
} CostCentre;

typedef struct CostCentreStack_ {
    StgInt ccsID;               // unique ID, allocated by the RTS

    CostCentre *cc;             // Cost centre at the top of the stack

    struct CostCentreStack_ *prevStack;   // parent
    struct IndexTable_      *indexTable;  // children
    struct CostCentreStack_ *root;        // root of stack
    StgWord    depth;           // number of items in the stack

    StgWord64  scc_count;       // Count of times this CCS is entered
                                // align 8 (Note [struct alignment])

    StgWord    selected;        // is this CCS shown in the heap
                                // profile? (zero if excluded via -hc
                                // -hm etc.)

    StgWord    time_ticks;      // number of time ticks accumulated by
                                // this CCS

    StgWord64  mem_alloc;       // mem allocated by this CCS
                                // align 8 (Note [struct alignment])

    StgWord64  inherited_alloc; // sum of mem_alloc over all children
                                // (calculated at the end)
                                // align 8 (Note [struct alignment])

    StgWord    inherited_ticks; // sum of time_ticks over all children
                                // (calculated at the end)
} CostCentreStack;


/* -----------------------------------------------------------------------------
 * Start and stop the profiling timer.  These can be called from
 * Haskell to restrict the profile to portion(s) of the execution.
 * See the module GHC.Profiling.
 * ---------------------------------------------------------------------------*/

void stopProfTimer      ( void );
void startProfTimer     ( void );

/* -----------------------------------------------------------------------------
 * The rest is PROFILING only...
 * ---------------------------------------------------------------------------*/

#if defined(PROFILING)

/* -----------------------------------------------------------------------------
 * Constants
 * ---------------------------------------------------------------------------*/

#define EMPTY_STACK NULL
#define EMPTY_TABLE NULL

/* Constants used to set is_caf flag on CostCentres */
#define CC_IS_CAF      true
#define CC_NOT_CAF     false
/* -----------------------------------------------------------------------------
 * Data Structures
 * ---------------------------------------------------------------------------*/

// IndexTable is the list of children of a CCS. (Alternatively it is a
// cache of the results of pushing onto a CCS, so that the second and
// subsequent times we push a certain CC on a CCS we get the same
// result).

typedef struct IndexTable_ {
    // Just a linked list of (cc, ccs) pairs, where the `ccs` is the result of
    // pushing `cc` to the owner of the index table (another CostCentreStack).
    CostCentre *cc;
    CostCentreStack *ccs;
    struct IndexTable_ *next;
    // back_edge is true when `cc` is already in the stack, so pushing it
    // truncates or drops (see RECURSION_DROPS and RECURSION_TRUNCATES in
    // Profiling.c).
    bool back_edge;
} IndexTable;


/* -----------------------------------------------------------------------------
   Pre-defined cost centres and cost centre stacks
   -------------------------------------------------------------------------- */

#if IN_STG_CODE

extern StgWord CC_MAIN[];
extern StgWord CCS_MAIN[];      // Top CCS

extern StgWord CC_SYSTEM[];
extern StgWord CCS_SYSTEM[];    // RTS costs

extern StgWord CC_GC[];
extern StgWord CCS_GC[];         // Garbage collector costs

extern StgWord CC_OVERHEAD[];
extern StgWord CCS_OVERHEAD[];   // Profiling overhead

extern StgWord CC_DONT_CARE[];
extern StgWord CCS_DONT_CARE[];  // CCS attached to static constructors

#else

extern CostCentre      CC_MAIN[];
extern CostCentreStack CCS_MAIN[];      // Top CCS

extern CostCentre      CC_SYSTEM[];
extern CostCentreStack CCS_SYSTEM[];    // RTS costs

extern CostCentre      CC_GC[];
extern CostCentreStack CCS_GC[];         // Garbage collector costs

extern CostCentre      CC_OVERHEAD[];
extern CostCentreStack CCS_OVERHEAD[];   // Profiling overhead

extern CostCentre      CC_DONT_CARE[];
extern CostCentreStack CCS_DONT_CARE[];  // shouldn't ever get set

extern CostCentre      CC_PINNED[];
extern CostCentreStack CCS_PINNED[];     // pinned memory

extern CostCentre      CC_IDLE[];
extern CostCentreStack CCS_IDLE[];       // capability is idle

#endif /* IN_STG_CODE */

extern unsigned int RTS_VAR(era);

/* -----------------------------------------------------------------------------
 * Functions
 * ---------------------------------------------------------------------------*/

CostCentreStack * pushCostCentre (CostCentreStack *, CostCentre *);
void              enterFunCCS    (StgRegTable *reg, CostCentreStack *);
CostCentre *mkCostCentre (char *label, char *module, char *srcloc);

extern CostCentre * RTS_VAR(CC_LIST);               // registered CC list

/* -----------------------------------------------------------------------------
 * Declaring Cost Centres & Cost Centre Stacks.
 * -------------------------------------------------------------------------- */

# define CC_DECLARE(cc_ident,name,mod,loc,caf,is_local)  \
     is_local CostCentre cc_ident[1]                     \
       = {{ .ccID       = 0,                             \
            .label      = name,                          \
            .module     = mod,                           \
            .srcloc     = loc,                           \
            .time_ticks = 0,                             \
            .mem_alloc  = 0,                             \
            .link       = 0,                             \
            .is_caf     = caf                            \
         }};

# define CCS_DECLARE(ccs_ident,cc_ident,is_local)        \
     is_local CostCentreStack ccs_ident[1]               \
       = {{ .ccsID               = 0,                    \
            .cc                  = cc_ident,             \
            .prevStack           = NULL,                 \
            .indexTable          = NULL,                 \
            .root                = NULL,                 \
            .depth               = 0,                    \
            .selected            = 0,                    \
            .scc_count           = 0,                    \
            .time_ticks          = 0,                    \
            .mem_alloc           = 0,                    \
            .inherited_ticks     = 0,                    \
            .inherited_alloc     = 0                     \
       }};

/* -----------------------------------------------------------------------------
 * Time / Allocation Macros
 * ---------------------------------------------------------------------------*/

/* eliminate profiling overhead from allocation costs */
#define CCS_ALLOC(ccs, size) (ccs)->mem_alloc += ((size)-sizeofW(StgProfHeader))
#define ENTER_CCS_THUNK(cap,p) cap->r.rCCCS = p->header.prof.ccs

#else /* !PROFILING */

#define CCS_ALLOC(ccs, amount) doNothing()
#define ENTER_CCS_THUNK(cap,p) doNothing()

#endif /* PROFILING */
