Commit dbef766c authored by simonmar's avatar simonmar

[project @ 2001-11-26 16:54:21 by simonmar]

Profiling cleanup.

This commit eliminates some duplication in the various heap profiling
subsystems, and generally centralises much of the machinery.  The key
concept is the separation of a heap *census* (which is now done in one
place only instead of three) from the calculation of retainer sets.
Previously the retainer profiling code also did a heap census on the
fly, and lag-drag-void profiling had its own census machinery.

Value-adds:

   - you can now restrict a heap profile to certain retainer sets,
     but still display by cost centre (or type, or closure or
     whatever).

   - I've added an option to restrict the maximum retainer set size
     (+RTS -R<size>, defaulting to 8).

   - I've cleaned up the heap profiling options at the request of
     Simon PJ.  See the help text for details.  The new scheme
     is backwards compatible with the old.

   - I've removed some odd bits of LDV or retainer profiling-specific
     code from various parts of the system.

   - the time taken doing heap censuses (and retainer set calculation)
     is now accurately reported by the RTS when you say +RTS -Sstderr.

Still to come:

   - restricting a profile to a particular biography
     (lag/drag/void/use).  This requires keeping old heap censuses
     around, but the infrastructure is now in place to do this.
parent 5680ea4b
/* ----------------------------------------------------------------------------
* $Id: Closures.h,v 1.29 2001/11/22 14:25:11 simonmar Exp $
* $Id: Closures.h,v 1.30 2001/11/26 16:54:22 simonmar Exp $
*
* (c) The GHC Team, 1998-1999
*
......@@ -22,7 +22,7 @@
typedef struct {
CostCentreStack *ccs;
union {
RetainerSet *rs; // Retainer Set
struct _RetainerSet *rs; // Retainer Set
StgWord ldvw; // Lag/Drag/Void Word
} hp;
} StgProfHeader;
......
/* ----------------------------------------------------------------------------
* $Id: Constants.h,v 1.18 2001/10/03 13:57:42 simonmar Exp $
* $Id: Constants.h,v 1.19 2001/11/26 16:54:22 simonmar Exp $
*
* (c) The GHC Team, 1998-1999
*
......@@ -144,7 +144,7 @@
-------------------------------------------------------------------------- */
/* The size of a block (2^BLOCK_SHIFT bytes) */
#define BLOCK_SHIFT 12
#define BLOCK_SHIFT 11
/* The size of a megablock (2^MBLOCK_SHIFT bytes) */
#define MBLOCK_SHIFT 20
......
/* -----------------------------------------------------------------------------
* $Id: RtsFlags.h,v 1.39 2001/11/25 16:57:38 sof Exp $
* $Id: RtsFlags.h,v 1.40 2001/11/26 16:54:22 simonmar Exp $
*
* (c) The GHC Team, 1998-1999
*
......@@ -97,22 +97,19 @@ struct PROFILING_FLAGS {
# define HEAP_BY_MOD 2
# define HEAP_BY_DESCR 4
# define HEAP_BY_TYPE 5
/* Flags for retainer and lag-drag-void profiling */
# define HEAP_BY_RETAINER 6
# define HEAP_BY_LDV 7
rtsBool showCCSOnException;
# define CCchar 'C'
# define MODchar 'M'
# define DESCRchar 'D'
# define TYPEchar 'Y'
nat maxRetainerSetSize;
char* modSelector;
char* descrSelector;
char* typeSelector;
char* ccSelector;
char* retainerSelector;
char* bioSelector;
};
#elif defined(DEBUG)
......
/* -----------------------------------------------------------------------------
* $Id: Stg.h,v 1.41 2001/11/25 03:56:39 sof Exp $
* $Id: Stg.h,v 1.42 2001/11/26 16:54:22 simonmar Exp $
*
* (c) The GHC Team, 1998-1999
*
......@@ -158,7 +158,6 @@ typedef StgWord64 LW_;
/* Profiling information */
#include "StgProf.h"
#include "StgRetainerProf.h"
#include "StgLdvProf.h"
/* Storage format definitions */
......
/* -----------------------------------------------------------------------------
* $Id: StgLdvProf.h,v 1.1 2001/11/22 14:25:11 simonmar Exp $
* $Id: StgLdvProf.h,v 1.2 2001/11/26 16:54:22 simonmar Exp $
*
* (c) The GHC Team, 2001
* Author: Sungwoo Park
......@@ -11,53 +11,6 @@
#ifndef STGLDVPROF_H
#define STGLDVPROF_H
#ifdef PROFILING
// Engine
// declared in LdvProfile.c
extern nat ldvTime;
// LdvGenInfo stores the statistics for one specific census.
typedef struct {
double time; // the time in MUT time at the corresponding census is made
// We employ int instead of nat, for some values may be negative temporarily,
// e.g., dragNew.
// computed at each census
int inherentlyUsed; // total size of 'inherently used' closures
int notUsed; // total size of 'never used' closures
int used; // total size of 'used at least once' closures
/*
voidNew and dragNew are updated when a closure is destroyed.
For instance, when a 'never used' closure of size s and creation time
t is destroyed at time u, voidNew of eras t through u - 1 is increased
by s.
Likewise, when a 'used at least once' closure of size s and last use time
t is destroyed at time u, dragNew of eras t + 1 through u - 1 is increase
by s.
In our implementation, voidNew and dragNew are computed indirectly: instead
of updating voidNew or dragNew of all intervening eras, we update that
of the end two eras (one is increased and the other is decreased).
*/
int voidNew; // current total size of 'destroyed without being used' closures
int dragNew; // current total size of 'used at least once and waiting to die'
// closures
// computed post-mortem
int voidTotal; // total size of closures in 'void' state
// lagTotal == notUsed - voidTotal // in 'lag' state
int dragTotal; // total size of closures in 'drag' state
// useTotal == used - dragTotal // in 'use' state
} LdvGenInfo;
extern LdvGenInfo *gi;
// retrieves the LDV word from closure c
#define LDVW(c) (((StgClosure *)(c))->header.prof.hp.ldvw)
/*
An LDV word is divided into 3 parts: state bits (LDV_STATE_MASK), creation
time bits (LDV_CREATE_MASK), and last use time bits (LDV_LAST_MASK).
......@@ -78,6 +31,13 @@ extern LdvGenInfo *gi;
#define LDV_STATE_USE 0x40000000
#endif // SIZEOF_VOID_P
#ifdef PROFILING
extern nat era;
// retrieves the LDV word from closure c
#define LDVW(c) (((StgClosure *)(c))->header.prof.hp.ldvw)
// Stores the creation time for closure c.
// This macro is called at the very moment of closure creation.
//
......@@ -86,38 +46,19 @@ extern LdvGenInfo *gi;
// because retainer profiling also expects LDVW(c) to be initialised
// to zero.
#define LDV_recordCreate(c) \
LDVW((c)) = (ldvTime << LDV_SHIFT) | LDV_STATE_CREATE
LDVW((c)) = (era << LDV_SHIFT) | LDV_STATE_CREATE
// Stores the last use time for closure c.
// This macro *must* be called whenever a closure is used, that is, it is
// entered.
#define LDV_recordUse(c) \
{ \
if (ldvTime > 0) \
LDVW((c)) = (LDVW((c)) & LDV_CREATE_MASK) | \
ldvTime | \
LDV_STATE_USE; \
#define LDV_recordUse(c) \
{ \
if (era > 0) \
LDVW((c)) = (LDVW((c)) & LDV_CREATE_MASK) | \
era | \
LDV_STATE_USE; \
}
// Creates a 0-filled slop of size 'howManyBackwards' backwards from the
// address 'from'.
//
// Invoked when:
// 1) Hp is incremented and exceeds HpLim (in Updates.hc).
// 2) copypart() is called (in GC.c).
#define FILL_SLOP(from, howManyBackwards) \
if (ldvTime > 0) { \
int i; \
for (i = 0;i < (howManyBackwards); i++) \
((StgWord *)(from))[-i] = 0; \
}
// Informs the LDV profiler that closure c has just been evacuated.
// Evacuated objects are no longer needed, so we just store its original size in
// the LDV field.
#define SET_EVACUAEE_FOR_LDV(c, size) \
LDVW((c)) = (size)
// Macros called when a closure is entered.
// The closure is not an 'inherently used' one.
// The closure is not IND or IND_OLDGEN because neither is considered for LDV
......
/* -----------------------------------------------------------------------------
* $Id: StgProf.h,v 1.14 2001/11/22 14:25:11 simonmar Exp $
* $Id: StgProf.h,v 1.15 2001/11/26 16:54:22 simonmar Exp $
*
* (c) The GHC Team, 1998
*
......@@ -39,7 +39,6 @@ typedef struct _CostCentreStack {
unsigned long time_ticks;
unsigned long long mem_alloc;
unsigned long mem_resid;
unsigned long inherited_ticks;
unsigned long long inherited_alloc;
......@@ -184,7 +183,6 @@ extern CostCentreStack *CCS_LIST; /* registered CCS list */
scc_count : 0, \
time_ticks : 0, \
mem_alloc : 0, \
mem_resid : 0, \
inherited_ticks : 0, \
inherited_alloc : 0, \
root : 0, \
......
/* -----------------------------------------------------------------------------
* $Id: StgRetainerProf.h,v 1.1 2001/11/22 14:25:12 simonmar Exp $
*
* (c) The GHC Team, 2001
*
* Retainer profiling
* ---------------------------------------------------------------------------*/
#ifndef STGRETAINERPROF_H
#define STGRETAINERPROF_H
/*
Type 'retainer' defines the retainer identity.
Invariant:
1. The retainer identity of a given retainer cannot change during
program execution, no matter where it is actually stored.
For instance, the memory address of a retainer cannot be used as
its retainer identity because its location may change during garbage
collections.
2. Type 'retainer' must come with comparison operations as well as
an equality operation. That it, <, >, and == must be supported -
this is necessary to store retainers in a sorted order in retainer sets.
Therefore, you cannot use a huge structure type as 'retainer', for instance.
We illustrate three possibilities of defining 'retainer identity'.
Choose one of the following three compiler directives:
Retainer scheme 1 (RETAINER_SCHEME_INFO) : retainer = info table
Retainer scheme 2 (RETAINER_SCHEME_CCS) : retainer = cost centre stack
Retainer scheme 3 (RETAINER_SCHEME_CC) : retainer = cost centre
*/
// #define RETAINER_SCHEME_INFO
#define RETAINER_SCHEME_CCS
// #define RETAINER_SCHEME_CC
#ifdef RETAINER_SCHEME_INFO
struct _StgInfoTable;
typedef struct _StgInfoTable *retainer;
#endif
#ifdef RETAINER_SCHEME_CCS
typedef CostCentreStack *retainer;
#endif
#ifdef RETAINER_SCHEME_CC
typedef CostCentre *retainer;
#endif
/*
Type 'retainerSet' defines an abstract datatype for sets of retainers.
Invariants:
A retainer set stores its elements in increasing order (in element[] array).
*/
typedef struct _RetainerSet {
nat num; // number of elements
nat cost; // cost associated with this retainer set
StgWord hashKey; // hash key for this retainer set
struct _RetainerSet *link; // link to the next retainer set in the bucket
int id; // unique id of this retainer set (used when printing)
// Its absolute value is interpreted as its true id; if id is
// negative, it indicates that this retainer set has had a postive
// cost after some retainer profiling.
retainer element[0]; // elements of this retainer set
// do not put anything below here!
} RetainerSet;
//
// retainerSet - interface: see rts/RetainerSet.h
//
#endif /* STGRETAINERPROF_H */
/* -----------------------------------------------------------------------------
* $Id: GC.c,v 1.127 2001/11/22 14:25:12 simonmar Exp $
* $Id: GC.c,v 1.128 2001/11/26 16:54:21 simonmar Exp $
*
* (c) The GHC Team 1998-1999
*
......@@ -1320,7 +1320,6 @@ copy(StgClosure *src, nat size, step *stp)
stp->hp = to;
upd_evacuee(src,(StgClosure *)dest);
#ifdef PROFILING
// @LDV profiling
// We store the size of the just evacuated object in the LDV word so that
// the profiler can guess the position of the next object later.
SET_EVACUAEE_FOR_LDV(src, size_org);
......@@ -1364,7 +1363,6 @@ copyPart(StgClosure *src, nat size_to_reserve, nat size_to_copy, step *stp)
stp->hp += size_to_reserve;
upd_evacuee(src,(StgClosure *)dest);
#ifdef PROFILING
// @LDV profiling
// We store the size of the just evacuated object in the LDV word so that
// the profiler can guess the position of the next object later.
// size_to_copy_org is wrong because the closure already occupies size_to_reserve
......
This diff is collapsed.
/* -----------------------------------------------------------------------------
* $Id: LdvProfile.h,v 1.1 2001/11/22 14:25:12 simonmar Exp $
* $Id: LdvProfile.h,v 1.2 2001/11/26 16:54:21 simonmar Exp $
*
* (c) The GHC Team, 2001
* Author: Sungwoo Park
......@@ -15,49 +15,29 @@
#include "ProfHeap.h"
void LDV_recordDead_FILL_SLOP_DYNAMIC( StgClosure *p );
// Precesses a closure 'c' being destroyed whose size is 'size'.
// Make sure that LDV_recordDead() is not invoked on 'inherently used' closures
// such as TSO; they should not be involved in computing dragNew or voidNew.
//
// Note: ldvTime is 0 if LDV profiling is turned off.
// ldvTime is > 0 if LDV profiling is turned on.
// size does not include StgProfHeader.
//
// Even though ldvTime is checked in both LdvCensusForDead() and
// LdvCensusKillAll(), we still need to make sure that ldvTime is > 0 because
// LDV_recordDead() may be called from elsewhere in the runtime system. E.g.,
// when a thunk is replaced by an indirection object.
static inline void
LDV_recordDead( StgClosure *c, nat size )
{
if (ldvTime > 0 && closureSatisfiesConstraints(c)) {
nat t;
size -= sizeofW(StgProfHeader);
if ((LDVW((c)) & LDV_STATE_MASK) == LDV_STATE_CREATE) {
t = (LDVW((c)) & LDV_CREATE_MASK) >> LDV_SHIFT;
if (t < ldvTime) {
gi[t].voidNew += (int)size;
gi[ldvTime].voidNew -= (int)size;
}
} else {
t = LDVW((c)) & LDV_LAST_MASK;
if (t + 1 < ldvTime) {
gi[t + 1].dragNew += size;
gi[ldvTime].dragNew -= size;
}
}
}
}
extern void initLdvProfiling ( void );
extern void endLdvProfiling ( void );
extern void LdvCensus ( void );
extern void LDV_recordDead_FILL_SLOP_DYNAMIC( StgClosure *p );
extern void LdvCensusForDead ( nat );
extern void LdvCensusKillAll ( void );
// Creates a 0-filled slop of size 'howManyBackwards' backwards from the
// address 'from'.
//
// Invoked when:
// 1) Hp is incremented and exceeds HpLim (in Updates.hc).
// 2) copypart() is called (in GC.c).
#define FILL_SLOP(from, howManyBackwards) \
if (era > 0) { \
int i; \
for (i = 0;i < (howManyBackwards); i++) \
((StgWord *)(from))[-i] = 0; \
}
// Informs the LDV profiler that closure c has just been evacuated.
// Evacuated objects are no longer needed, so we just store its original size in
// the LDV field.
#define SET_EVACUAEE_FOR_LDV(c, size) \
LDVW((c)) = (size)
#endif /* PROFILING */
#endif /* LDVPROFILE_H */
This diff is collapsed.
/* -----------------------------------------------------------------------------
* $Id: ProfHeap.h,v 1.2 2001/11/22 14:25:12 simonmar Exp $
* $Id: ProfHeap.h,v 1.3 2001/11/26 16:54:21 simonmar Exp $
*
* (c) The GHC Team, 1998-1999
*
......@@ -7,8 +7,13 @@
*
* ---------------------------------------------------------------------------*/
#ifndef PROFHEAP_H
#define PROFHEAP_H
extern void heapCensus( void );
extern nat initHeapProfiling( void );
extern void endHeapProfiling( void );
extern rtsBool closureSatisfiesConstraints( StgClosure* p );
extern void LDV_recordDead( StgClosure *c, nat size );
#endif
/* -----------------------------------------------------------------------------
* $Id: Profiling.c,v 1.26 2001/11/22 16:33:06 simonmar Exp $
* $Id: Profiling.c,v 1.27 2001/11/26 16:54:21 simonmar Exp $
*
* (c) The GHC Team, 1998-2000
*
......@@ -194,15 +194,6 @@ initProfiling1 (void)
/* cost centres are registered by the per-module
* initialisation code now...
*/
switch (RtsFlags.ProfFlags.doHeapProfile) {
case HEAP_BY_RETAINER:
initRetainerProfiling();
break;
case HEAP_BY_LDV:
initLdvProfiling();
break;
}
}
void
......
/* -----------------------------------------------------------------------------
* $Id: RetainerProfile.c,v 1.1 2001/11/22 14:25:12 simonmar Exp $
* $Id: RetainerProfile.c,v 1.2 2001/11/26 16:54:21 simonmar Exp $
*
* (c) The GHC Team, 2001
* Author: Sungwoo Park
......@@ -54,15 +54,9 @@ static nat timesAnyObjectVisited; // number of times any objects are visited
pointer. See retainerSetOf().
*/
// extract the retainer set field from c
#define RSET(c) ((c)->header.prof.hp.rs)
static StgWord flip = 0; // flip bit
StgWord flip = 0; // flip bit
// must be 0 if DEBUG_RETAINER is on (for static closures)
#define isRetainerSetFieldValid(c) \
((((StgWord)(c)->header.prof.hp.rs & 1) ^ flip) == 0)
#define setRetainerSetToNull(c) \
(c)->header.prof.hp.rs = (RetainerSet *)((StgWord)NULL | flip)
......@@ -870,159 +864,6 @@ maybeInitRetainerSet( StgClosure *c )
}
}
static inline RetainerSet *
retainerSetOf( StgClosure *c )
{
ASSERT( isRetainerSetFieldValid(c) );
// StgWord has the same size as pointers, so the following type
// casting is okay.
return (RetainerSet *)((StgWord)RSET(c) ^ flip);
}
/* -----------------------------------------------------------------------------
* Returns the cost of the closure *c, e.g., the amount of heap memory
* allocated to *c. Static objects cost 0.
* The cost includes even the words allocated for profiling purpose.
* Cf. costPure().
* -------------------------------------------------------------------------- */
static inline nat
cost( StgClosure *c )
{
StgInfoTable *info;
info = get_itbl(c);
switch (info->type) {
case TSO:
return tso_sizeW((StgTSO *)c);
case THUNK:
case THUNK_1_0:
case THUNK_0_1:
case THUNK_2_0:
case THUNK_1_1:
case THUNK_0_2:
return stg_max(sizeW_fromITBL(info), sizeofW(StgHeader) + MIN_UPD_SIZE);
// static objects
case CONSTR_STATIC:
case FUN_STATIC:
case THUNK_STATIC:
return 0;
case MVAR:
return sizeofW(StgMVar);
case MUT_ARR_PTRS:
case MUT_ARR_PTRS_FROZEN:
return mut_arr_ptrs_sizeW((StgMutArrPtrs *)c);
case AP_UPD:
case PAP:
return pap_sizeW((StgPAP *)c);
case ARR_WORDS:
return arr_words_sizeW((StgArrWords *)c);
case CONSTR:
case CONSTR_1_0:
case CONSTR_0_1:
case CONSTR_2_0:
case CONSTR_1_1:
case CONSTR_0_2:
case FUN:
case FUN_1_0:
case FUN_0_1:
case FUN_2_0:
case FUN_1_1:
case FUN_0_2:
case WEAK:
case MUT_VAR:
case MUT_CONS:
case CAF_BLACKHOLE:
case BLACKHOLE:
case SE_BLACKHOLE:
case SE_CAF_BLACKHOLE:
case BLACKHOLE_BQ:
case IND_PERM:
case IND_OLDGEN:
case IND_OLDGEN_PERM:
case FOREIGN:
case BCO:
case STABLE_NAME:
return sizeW_fromITBL(info);
case THUNK_SELECTOR:
return sizeofW(StgHeader) + MIN_UPD_SIZE;
/*
Error case
*/
// IND_STATIC cannot be *c, *cp, *r in the retainer profiling loop.
case IND_STATIC:
// CONSTR_INTLIKE, CONSTR_CHARLIKE, and CONSTR_NOCAF_STATIC
// cannot be *c, *cp, *r in the retainer profiling loop.
case CONSTR_INTLIKE:
case CONSTR_CHARLIKE:
case CONSTR_NOCAF_STATIC:
// Stack objects are invalid because they are never treated as
// legal objects during retainer profiling.
case UPDATE_FRAME:
case CATCH_FRAME:
case STOP_FRAME:
case SEQ_FRAME:
case RET_DYN:
case RET_BCO:
case RET_SMALL:
case RET_VEC_SMALL:
case RET_BIG:
case RET_VEC_BIG:
// other cases
case IND:
case BLOCKED_FETCH:
case FETCH_ME:
case FETCH_ME_BQ:
case RBH:
case REMOTE_REF:
case EVACUATED:
case INVALID_OBJECT:
default:
barf("Invalid object in cost(): %d", get_itbl(c)->type);
}
}
/* -----------------------------------------------------------------------------
* Returns the pure cost of the closure *c, i.e., the size of memory
* allocated for this object without profiling.
* Note & Todo:
* costPure() subtracts the overhead incurred by profiling for all types
* of objects except TSO. Even though the overhead in the TSO object
* itself is taken into account, the additional costs due to larger
* stack objects (with unnecessary retainer profiling fields) is not
* considered. Still, costPure() should result in an accurate estimate
* of heap use because stacks in TSO objects are allocated in large blocks.
* If we get rid of the (currently unused) retainer profiling field in
* all stack objects, the result will be accurate.
* ------------------------------------------------------------------------- */
static inline nat
costPure( StgClosure *c )
{
nat cst;
if (!closureSatisfiesConstraints(c)) {
return 0;
}
cst = cost(c);
ASSERT(cst == 0 || cst - sizeofW(StgProfHeader) > 0);
if (cst > 0) {
return cst - sizeofW(StgProfHeader);
} else {
return 0;
}
}
/* -----------------------------------------------------------------------------
* Returns rtsTrue if *c is a retainer.
* -------------------------------------------------------------------------- */
......@@ -1149,7 +990,7 @@ isRetainer( StgClosure *c )
* Depending on the definition of this function, the maintenance of retainer
* sets can be made easier. If most retainer sets are likely to be created
* again across garbage collections, refreshAllRetainerSet() in
* RetainerSet.c can simply set the cost field of each retainer set.
* RetainerSet.c can simply do nothing.
* If this is not the case, we can free all the retainer sets and
* re-initialize the hash table.
* See refreshAllRetainerSet() in RetainerSet.c.
......@@ -1179,19 +1020,11 @@ getRetainerFrom( StgClosure *c )
* s != NULL
* -------------------------------------------------------------------------- */
static inline void
associate( StgClosure *c, RetainerSet *rsOfc, RetainerSet *s )
associate( StgClosure *c, RetainerSet *s )
{
nat cost_c;
cost_c = costPure(c); // not cost(c)
if (rsOfc != NULL) {
ASSERT(rsOfc->cost >= cost_c);
rsOfc->cost -= cost_c;
}
// StgWord has the same size as pointers, so the following type
// casting is okay.
RSET(c) = (RetainerSet *)((StgWord)s | flip);
s->cost += cost_c;
}
/* -----------------------------------------------------------------------------
......@@ -1566,10 +1399,10 @@ inner_loop:
numObjectVisited++;
if (s == NULL)
associate(c, NULL, singleton(R_r));
associate(c, singleton(R_r));
else
// s is actually the retainer set of *c!
associate(c, NULL, s);
associate(c, s);
// compute c_child_r
c_child_r = isRetainer(c) ? c : r;
......@@ -1579,18 +1412,18 @@ inner_loop:
goto loop; // no need to process child
if (s == NULL)
associate(c, retainerSetOfc, addElement(R_r, retainerSetOfc));
associate(c, addElement(R_r, retainerSetOfc));
else {
// s is not NULL and cp is not a retainer. This means that
// each time *cp is visited, so is *c. Thus, if s has
// exactly one more element in its retainer set than c, s
// is also the new retainer set for *c.
if (s->num == retainerSetOfc->num + 1) {
associate(c, retainerSetOfc, s);
associate(c, s);
}
// Otherwise, just add R_r to the current retainer set of *c.
else {
associate(c, retainerSetOfc, addElement(R_r, retainerSetOfc));
associate(c, addElement(R_r, retainerSetOfc));
}
}
......@@ -1846,7 +1679,6 @@ resetStaticObjectForRetainerProfiling( void )
void
retainerProfile(void)
{
nat allCost, numSet;
#ifdef DEBUG_RETAINER
nat i;
nat totalHeapSize; // total raw heap size (computed by linear scanning)
......@@ -1923,8 +1755,6 @@ retainerProfile(void)
#endif
computeRetainerSet();
outputRetainerSet(hp_file, &allCost, &numSet);
#ifdef DEBUG_RETAINER
fprintf(stderr, "After traversing:\n");
sumOfCostLinear = 0;
......@@ -1978,8 +1808,7 @@ retainerProfile(void)
#ifdef DEBUG_RETAINER
maxCStackSize, maxStackSize,
#endif
(double)timesAnyObjectVisited / numObjectVisited,
allCost, numSet);
(double)timesAnyObjectVisited / numObjectVisited);
}
/* -----------------------------------------------------------------------------
......
/* -----------------------------------------------------------------------------
* $Id: RetainerProfile.h,v 1.1 2001/11/22 14:25:12 simonmar Exp $
* $Id: RetainerProfile.h,v 1.2 2001/11/26 16:54:21 simonmar Exp $
*
* (c) The GHC Team, 2001
* Author: Sungwoo Park
......@@ -13,12 +13,31 @@
#ifdef PROFILING
#include "RetainerSet.h"
extern void initRetainerProfiling ( void );
extern void endRetainerProfiling ( void );
extern void printRetainer ( FILE *, retainer );
extern void retainerProfile ( void );
extern void resetStaticObjectForRetainerProfiling ( void );
extern StgWord flip;
// extract the retainer set field from c
#define RSET(c) ((c)->header.prof.hp.rs)
#define isRetainerSetFieldValid(c) \
((((StgWord)(c)->header.prof.hp.rs & 1) ^ flip) == 0)
static inline RetainerSet *
retainerSetOf( StgClosure *c )
{
ASSERT( isRetainerSetFieldValid(c) );
// StgWord has the same size as pointers, so the following type
// casting is okay.
return (RetainerSet *)((StgWord)RSET(c) ^ flip);
}
// firstStack is exported because memInventory() in Schedule.c uses it.
#ifdef DEBUG
extern bdescr *firstStack;
......
/* -----------------------------------------------------------------------------
* $Id: RetainerSet.c,v 1.1 2001/11/22 14:25:12 simonmar Exp $