Commit cbeb99ef authored by Simon Marlow's avatar Simon Marlow

Basic heap profile support without -prof

Now that constructor info tables contain the name of the constructor,
we can generate useful heap profiles without requiring the whole
program and libraries to be compiled with -prof.  So now, "+RTS -hT"
generates a heap profile for any program, dividing the profile by
constructor.  It wouldn't be hard to add support for grouping
constructors by module, or to restrict the profile to certain
constructors/modules/packages.

This means that for the first time we can get heap profiles for GHCi,
which was previously impossible because the byte-code
interpreter and linker don't work with -prof.
parent 47e0b5e5
......@@ -411,6 +411,16 @@ typedef struct _StgConInfoTable {
#define GET_SRT(info) ((info)->srt)
#endif
/*
* GET_CON_DESC(info)
* info must be a StgConInfoTable*.
*/
#ifdef TABLES_NEXT_TO_CODE
#define GET_CON_DESC(info) ((char *)((StgWord)((info)+1) + (info->con_desc)))
#else
#define GET_CON_DESC(info) ((info)->con_desc)
#endif
/*
* GET_FUN_SRT(info)
* info must be a StgFunInfoTable*
......
......@@ -86,8 +86,7 @@ struct PROFILING_FLAGS {
# define HEAP_BY_RETAINER 6
# define HEAP_BY_LDV 7
# define HEAP_BY_INFOPTR 1 /* DEBUG only */
# define HEAP_BY_CLOSURE_TYPE 2 /* DEBUG only */
# define HEAP_BY_CLOSURE_TYPE 8
nat profileInterval; /* delta between samples (in ms) */
nat profileIntervalTicks; /* delta between samples (in 'ticks') */
......
......@@ -42,7 +42,7 @@ newArena( void )
Arena *arena;
arena = stgMallocBytes(sizeof(Arena), "newArena");
arena->current = allocBlock();
arena->current = allocBlock_lock();
arena->current->link = NULL;
arena->free = arena->current->start;
arena->lim = arena->current->start + BLOCK_SIZE_W;
......@@ -81,7 +81,7 @@ arenaAlloc( Arena *arena, size_t size )
} else {
// allocate a fresh block...
req_blocks = (lnat)BLOCK_ROUND_UP(size) / BLOCK_SIZE;
bd = allocGroup(req_blocks);
bd = allocGroup_lock(req_blocks);
arena_blocks += req_blocks;
bd->gen_no = 0;
......@@ -106,7 +106,7 @@ arenaFree( Arena *arena )
next = bd->link;
arena_blocks -= bd->blocks;
ASSERT(arena_blocks >= 0);
freeGroup(bd);
freeGroup_lock(bd);
}
stgFree(arena);
}
......
......@@ -35,7 +35,7 @@ WAYS=$(GhcLibWays) $(GhcRTSWays)
ifneq "$(findstring debug, $(way))" ""
GhcRtsHcOpts=
GhcRtsCcOpts=-g
GhcRtsCcOpts=-g -O0
endif
# -----------------------------------------------------------------------------
......
/* -----------------------------------------------------------------------------
/* ----------------------------------------------------------------------------
*
* (c) The GHC Team, 1998-2003
*
* Support for heap profiling
*
* ---------------------------------------------------------------------------*/
#if defined(DEBUG) && !defined(PROFILING)
#define DEBUG_HEAP_PROF
#else
#undef DEBUG_HEAP_PROF
#endif
#if defined(PROFILING) || defined(DEBUG_HEAP_PROF)
* --------------------------------------------------------------------------*/
#include "PosixSource.h"
#include "Rts.h"
......@@ -103,70 +95,86 @@ static void aggregateCensusInfo( void );
static void dumpCensus( Census *census );
/* -----------------------------------------------------------------------------
/* ----------------------------------------------------------------------------
Closure Type Profiling;
------------------------------------------------------------------------- */
PROBABLY TOTALLY OUT OF DATE -- ToDo (SDM)
-------------------------------------------------------------------------- */
#ifdef DEBUG_HEAP_PROF
static char *type_names[] = {
"INVALID_OBJECT"
, "CONSTR"
, "CONSTR_STATIC"
, "CONSTR_NOCAF_STATIC"
, "FUN"
, "FUN_STATIC"
, "THUNK"
, "THUNK_STATIC"
, "THUNK_SELECTOR"
, "BCO"
, "AP_STACK"
, "AP"
, "PAP"
, "IND"
, "IND_OLDGEN"
, "IND_PERM"
, "IND_OLDGEN_PERM"
, "IND_STATIC"
, "RET_BCO"
, "RET_SMALL"
, "RET_BIG"
, "RET_DYN"
, "UPDATE_FRAME"
, "CATCH_FRAME"
, "STOP_FRAME"
, "BLACKHOLE"
, "MVAR"
, "ARR_WORDS"
, "MUT_ARR_PTRS_CLEAN"
, "MUT_ARR_PTRS_DIRTY"
, "MUT_ARR_PTRS_FROZEN"
, "MUT_VAR_CLEAN"
, "MUT_VAR_DIRTY"
, "WEAK"
, "TSO"
, "BLOCKED_FETCH"
, "FETCH_ME"
, "EVACUATED"
};
#endif /* DEBUG_HEAP_PROF */
/* -----------------------------------------------------------------------------
"INVALID_OBJECT",
"CONSTR",
"CONSTR_1_0",
"CONSTR_0_1",
"CONSTR_2_0",
"CONSTR_1_1",
"CONSTR_0_2",
"CONSTR_STATIC",
"CONSTR_NOCAF_STATIC",
"FUN",
"FUN_1_0",
"FUN_0_1",
"FUN_2_0",
"FUN_1_1",
"FUN_0_2",
"FUN_STATIC",
"THUNK",
"THUNK_1_0",
"THUNK_0_1",
"THUNK_2_0",
"THUNK_1_1",
"THUNK_0_2",
"THUNK_STATIC",
"THUNK_SELECTOR",
"BCO",
"AP",
"PAP",
"AP_STACK",
"IND",
"IND_OLDGEN",
"IND_PERM",
"IND_OLDGEN_PERM",
"IND_STATIC",
"RET_BCO",
"RET_SMALL",
"RET_BIG",
"RET_DYN",
"RET_FUN",
"UPDATE_FRAME",
"CATCH_FRAME",
"STOP_FRAME",
"CAF_BLACKHOLE",
"BLACKHOLE",
"SE_BLACKHOLE",
"SE_CAF_BLACKHOLE",
"MVAR",
"ARR_WORDS",
"MUT_ARR_PTRS_CLEAN",
"MUT_ARR_PTRS_DIRTY",
"MUT_ARR_PTRS_FROZEN0",
"MUT_ARR_PTRS_FROZEN",
"MUT_VAR_CLEAN",
"MUT_VAR_DIRTY",
"WEAK",
"STABLE_NAME",
"TSO",
"BLOCKED_FETCH",
"FETCH_ME",
"FETCH_ME_BQ",
"RBH",
"EVACUATED",
"REMOTE_REF",
"TVAR_WATCH_QUEUE",
"INVARIANT_CHECK_QUEUE",
"ATOMIC_INVARIANT",
"TVAR",
"TREC_CHUNK",
"TREC_HEADER",
"ATOMICALLY_FRAME",
"CATCH_RETRY_FRAME",
"CATCH_STM_FRAME",
"N_CLOSURE_TYPES"
};
/* ----------------------------------------------------------------------------
* Find the "closure identity", which is a unique pointer reresenting
* the band to which this closure's heap space is attributed in the
* heap profile.
......@@ -193,11 +201,26 @@ closureIdentity( StgClosure *p )
else
return NULL;
#else // DEBUG
case HEAP_BY_INFOPTR:
return (void *)((StgClosure *)p)->header.info;
#else
case HEAP_BY_CLOSURE_TYPE:
return type_names[get_itbl(p)->type];
{
StgInfoTable *info;
info = get_itbl(p);
switch (info->type) {
case CONSTR:
case CONSTR_1_0:
case CONSTR_0_1:
case CONSTR_2_0:
case CONSTR_1_1:
case CONSTR_0_2:
case CONSTR_STATIC:
case CONSTR_NOCAF_STATIC:
printf("",strlen(GET_CON_DESC(itbl_to_con_itbl(info))));
return GET_CON_DESC(itbl_to_con_itbl(info));
default:
return type_names[info->type];
}
}
#endif
default:
......@@ -300,6 +323,7 @@ LDV_recordDead( StgClosure *c, nat size )
/* --------------------------------------------------------------------------
* Initialize censuses[era];
* ----------------------------------------------------------------------- */
STATIC_INLINE void
initEra(Census *census)
{
......@@ -325,6 +349,7 @@ freeEra(Census *census)
* Increases era by 1 and initialize census[era].
* Reallocates gi[] and increases its size if needed.
* ----------------------------------------------------------------------- */
static void
nextEra( void )
{
......@@ -348,23 +373,23 @@ nextEra( void )
initEra( &censuses[era] );
}
/* -----------------------------------------------------------------------------
* DEBUG heap profiling, by info table
* -------------------------------------------------------------------------- */
/* ----------------------------------------------------------------------------
* Heap profiling by info table
* ------------------------------------------------------------------------- */
#ifdef DEBUG_HEAP_PROF
#if !defined(PROFILNG)
FILE *hp_file;
static char *hp_filename;
void initProfiling1( void )
void initProfiling1 (void)
{
}
void freeProfiling1( void )
void freeProfiling1 (void)
{
}
void initProfiling2( void )
void initProfiling2 (void)
{
if (RtsFlags.ProfFlags.doHeapProfile) {
/* Initialise the log file name */
......@@ -387,7 +412,7 @@ void endProfiling( void )
{
endHeapProfiling();
}
#endif /* DEBUG_HEAP_PROF */
#endif /* !PROFILING */
static void
printSample(rtsBool beginSample, StgDouble sampleValue)
......@@ -463,10 +488,6 @@ initHeapProfiling(void)
printSample(rtsTrue, 0);
printSample(rtsFalse, 0);
#ifdef DEBUG_HEAP_PROF
DEBUG_LoadSymbols(prog_name);
#endif
#ifdef PROFILING
if (doingRetainerProfiling()) {
initRetainerProfiling();
......@@ -603,7 +624,7 @@ strMatchesSelector( char* str, char* sel )
rtsBool
closureSatisfiesConstraints( StgClosure* p )
{
#ifdef DEBUG_HEAP_PROF
#if !defined(PROFILING)
(void)p; /* keep gcc -Wall happy */
return rtsTrue;
#else
......@@ -808,11 +829,8 @@ dumpCensus( Census *census )
if (count == 0) continue;
#ifdef DEBUG_HEAP_PROF
#if !defined(PROFILING)
switch (RtsFlags.ProfFlags.doHeapProfile) {
case HEAP_BY_INFOPTR:
fprintf(hp_file, "%s", lookupGHCName(ctr->identity));
break;
case HEAP_BY_CLOSURE_TYPE:
fprintf(hp_file, "%s", (char *)ctr->identity);
break;
......@@ -984,10 +1002,7 @@ heapCensusChain( Census *census, bdescr *bd )
case TSO:
prim = rtsTrue;
#ifdef DEBUG_HEAP_PROF
size = tso_sizeW((StgTSO *)p);
break;
#else
#ifdef PROFILING
if (RtsFlags.ProfFlags.includeTSOs) {
size = tso_sizeW((StgTSO *)p);
break;
......@@ -996,6 +1011,9 @@ heapCensusChain( Census *census, bdescr *bd )
p += tso_sizeW((StgTSO *)p);
continue;
}
#else
size = tso_sizeW((StgTSO *)p);
break;
#endif
case TREC_HEADER:
......@@ -1034,11 +1052,11 @@ heapCensusChain( Census *census, bdescr *bd )
identity = NULL;
#ifdef DEBUG_HEAP_PROF
real_size = size;
#else
#ifdef PROFILING
// subtract the profiling overhead
real_size = size - sizeofW(StgProfHeader);
#else
real_size = size;
#endif
if (closureSatisfiesConstraints((StgClosure*)p)) {
......@@ -1158,13 +1176,13 @@ heapCensus( void )
// future restriction by biography.
#ifdef PROFILING
if (RtsFlags.ProfFlags.bioSelector == NULL)
#endif
{
freeHashTable( census->hash, NULL/* don't free the elements */ );
arenaFree( census->arena );
census->hash = NULL;
census->arena = NULL;
}
#endif
// we're into the next time period now
nextEra();
......@@ -1174,5 +1192,3 @@ heapCensus( void )
#endif
}
#endif /* PROFILING || DEBUG_HEAP_PROF */
......@@ -11,15 +11,13 @@
#include <stdio.h>
#if defined(PROFILING) || defined(DEBUG)
void initProfiling1 ( void );
void freeProfiling1 ( void );
void initProfiling2 ( void );
void endProfiling ( void );
void initProfiling1 (void);
void freeProfiling1 (void);
void initProfiling2 (void);
void endProfiling (void);
extern FILE *prof_file;
extern FILE *hp_file;
#endif
#ifdef PROFILING
......
......@@ -6,8 +6,6 @@
*
* ---------------------------------------------------------------------------*/
#if defined (PROFILING)
#include "PosixSource.h"
#include "Rts.h"
......@@ -66,9 +64,11 @@ initProfTimer( void )
void
handleProfTick(void)
{
#ifdef PROFILING
if (do_prof_ticks) {
CCCS->time_ticks++;
}
#endif
if (do_heap_prof_ticks) {
ticks_to_heap_profile--;
......@@ -78,5 +78,3 @@ handleProfTick(void)
}
}
}
#endif /* PROFILING */
......@@ -172,9 +172,10 @@ void initRtsFlagsDefaults(void)
RtsFlags.CcFlags.doCostCentres = 0;
#endif /* PROFILING or PAR */
#ifdef PROFILING
RtsFlags.ProfFlags.doHeapProfile = rtsFalse;
RtsFlags.ProfFlags.profileInterval = 100;
#ifdef PROFILING
RtsFlags.ProfFlags.includeTSOs = rtsFalse;
RtsFlags.ProfFlags.showCCSOnException = rtsFalse;
RtsFlags.ProfFlags.maxRetainerSetSize = 8;
......@@ -186,9 +187,6 @@ void initRtsFlagsDefaults(void)
RtsFlags.ProfFlags.ccsSelector = NULL;
RtsFlags.ProfFlags.retainerSelector = NULL;
RtsFlags.ProfFlags.bioSelector = NULL;
#elif defined(DEBUG)
RtsFlags.ProfFlags.doHeapProfile = rtsFalse;
#endif
RtsFlags.MiscFlags.tickInterval = 50; /* In milliseconds */
......@@ -382,20 +380,17 @@ usage_text[] = {
" -L<chars> Maximum length of a cost-centre stack in a heap profile",
" (default: 25)",
"",
" -i<sec> Time between heap samples (seconds, default: 0.1)",
"",
" -xt Include threads (TSOs) in a heap profile",
"",
" -xc Show current cost centre stack on raising an exception",
"",
# endif
#endif /* PROFILING or PAR */
#if !defined(PROFILING) && defined(DEBUG)
#if !defined(PROFILING)
"",
" -h<break-down> Debugging Heap residency profile",
" (output file <program>.hp)",
" break-down: L = closure label (default)",
" T = closure type (constructor, thunk etc.)",
" -hT Heap residency profile (output file <program>.hp)",
#endif
" -i<sec> Time between heap samples (seconds, default: 0.1)",
"",
#if defined(TICKY_TICKY)
" -r<file> Produce ticky-ticky statistics (with -rstderr for stderr)",
......@@ -936,12 +931,9 @@ error = rtsTrue;
}
) break;
case 'h': /* serial heap profile */
#if !defined(PROFILING) && defined(DEBUG)
#if !defined(PROFILING)
switch (rts_argv[arg][2]) {
case '\0':
case 'L':
RtsFlags.ProfFlags.doHeapProfile = HEAP_BY_INFOPTR;
break;
case 'T':
RtsFlags.ProfFlags.doHeapProfile = HEAP_BY_CLOSURE_TYPE;
break;
......@@ -1057,7 +1049,6 @@ error = rtsTrue;
#endif /* PROFILING */
break;
#if defined(PROFILING)
case 'i': /* heap sample interval */
if (rts_argv[arg][2] == '\0') {
/* use default */
......@@ -1069,7 +1060,6 @@ error = rtsTrue;
RtsFlags.ProfFlags.profileInterval = cst;
}
break;
#endif
/* =========== CONCURRENT ========================= */
case 'C': /* context switch interval */
......@@ -1251,13 +1241,11 @@ error = rtsTrue;
RtsFlags.MiscFlags.tickInterval);
}
#ifdef PROFILING
if (RtsFlags.ProfFlags.profileInterval > 0) {
RtsFlags.MiscFlags.tickInterval =
stg_min(RtsFlags.ProfFlags.profileInterval,
RtsFlags.MiscFlags.tickInterval);
}
#endif
if (RtsFlags.ConcFlags.ctxtSwitchTime > 0) {
RtsFlags.ConcFlags.ctxtSwitchTicks =
......@@ -1267,10 +1255,8 @@ error = rtsTrue;
RtsFlags.ConcFlags.ctxtSwitchTicks = 0;
}
#ifdef PROFILING
RtsFlags.ProfFlags.profileIntervalTicks =
RtsFlags.ProfFlags.profileInterval / RtsFlags.MiscFlags.tickInterval;
#endif
if (error) {
const char **p;
......
......@@ -38,8 +38,9 @@
#include "FrontPanel.h"
#endif
#if defined(PROFILING) || defined(DEBUG)
# include "Profiling.h"
#if defined(PROFILING)
# include "ProfHeap.h"
# include "RetainerProfile.h"
#endif
......@@ -244,9 +245,7 @@ hs_init(int *argc, char **argv[])
initThreadLabelTable();
#endif
#if defined(PROFILING) || defined(DEBUG)
initProfiling1();
#endif
/* start the virtual timer 'subsystem'. */
startTimer();
......@@ -353,11 +352,9 @@ hs_add_root(void (*init_root)(void))
startupHpc();
#if defined(PROFILING) || defined(DEBUG)
// This must be done after module initialisation.
// ToDo: make this work in the presence of multiple hs_add_root()s.
initProfiling2();
#endif
}
/* -----------------------------------------------------------------------------
......@@ -455,9 +452,7 @@ hs_exit(void)
/* free the stable pointer table */
exitStablePtrTable();
#if defined(PROFILING) || defined(DEBUG)
freeProfiling1();
#endif
#if defined(DEBUG)
/* free the thread label table */
......@@ -477,9 +472,7 @@ hs_exit(void)
reportCCSProfiling();
#endif
#if defined(PROFILING) || defined(DEBUG)
endProfiling();
#endif
#ifdef PROFILING
// Originally, this was in report_ccs_profiling(). Now, retainer
......
......@@ -316,7 +316,6 @@ resetGenSymZh(void) /* it's your funeral */
Get the current time as a string. Used in profiling reports.
-------------------------------------------------------------------------- */
#if defined(PROFILING) || defined(DEBUG) || defined(PAR) || defined(GRAN)
char *
time_str(void)
{
......@@ -335,7 +334,6 @@ time_str(void)
}
return nowstr;
}
#endif
/* -----------------------------------------------------------------------------
* Reset a file handle to blocking mode. We do this for the standard
......
......@@ -28,10 +28,8 @@
#include "ThreadLabels.h"
#include "LdvProfile.h"
#include "Updates.h"
#ifdef PROFILING
#include "Proftimer.h"
#include "ProfHeap.h"
#endif
#if defined(GRAN) || defined(PARALLEL_HASKELL)
# include "GranSimRts.h"
# include "GranSim.h"
......@@ -216,7 +214,7 @@ static rtsBool scheduleHandleYield( Capability *cap, StgTSO *t,
static void scheduleHandleThreadBlocked( StgTSO *t );
static rtsBool scheduleHandleThreadFinished( Capability *cap, Task *task,
StgTSO *t );
static rtsBool scheduleDoHeapProfile(rtsBool ready_to_gc);
static rtsBool scheduleNeedHeapProfile(rtsBool ready_to_gc);
static Capability *scheduleDoGC(Capability *cap, Task *task,
rtsBool force_major);
......@@ -572,9 +570,7 @@ run_thread:
debugTrace(DEBUG_sched, "-->> running thread %ld %s ...",
(long)t->id, whatNext_strs[t->what_next]);
#if defined(PROFILING)
startHeapProfTimer();
#endif
// Check for exceptions blocked on this thread
maybePerformBlockedException (cap, t);
......@@ -667,8 +663,8 @@ run_thread:
// ----------------------------------------------------------------------
// Costs for the scheduler are assigned to CCS_SYSTEM
#if defined(PROFILING)
stopHeapProfTimer();
#if defined(PROFILING)
CCCS = CCS_SYSTEM;
#endif
......@@ -705,8 +701,7 @@ run_thread:
barf("schedule: invalid thread return code %d", (int)ret);
}
if (scheduleDoHeapProfile(ready_to_gc)) { ready_to_gc = rtsFalse; }
if (ready_to_gc) {
if (ready_to_gc || scheduleNeedHeapProfile(ready_to_gc)) {
cap = scheduleDoGC(cap,task,rtsFalse);
}
} /* end of while() */
......@@ -1920,36 +1915,21 @@ scheduleHandleThreadFinished (Capability *cap STG_UNUSED, Task *task, StgTSO *t)
}
/* -----------------------------------------------------------------------------
* Perform a heap census, if PROFILING
* Perform a heap census
* -------------------------------------------------------------------------- */
static rtsBool
scheduleDoHeapProfile( rtsBool ready_to_gc STG_UNUSED )
scheduleNeedHeapProfile( rtsBool ready_to_gc STG_UNUSED )
{
#if defined(PROFILING)
// When we have +RTS -i0 and we're heap profiling, do a census at
<