1 /* -----------------------------------------------------------------------------
2 * $Id: ProfHeap.c,v 1.27 2001/11/26 16:54:21 simonmar Exp $
4 * (c) The GHC Team, 1998-2000
6 * Support for heap profiling
8 * ---------------------------------------------------------------------------*/
10 #if defined(DEBUG) && !defined(PROFILING)
11 #define DEBUG_HEAP_PROF
13 #undef DEBUG_HEAP_PROF
16 #if defined(PROFILING) || defined(DEBUG_HEAP_PROF)
18 #include "PosixSource.h"
22 #include "Profiling.h"
28 #include "RetainerProfile.h"
29 #include "LdvProfile.h"
32 #ifdef DEBUG_HEAP_PROF
34 static void fprint_data(FILE *fp);
37 /* -----------------------------------------------------------------------------
38 * era stores the current time period. It is the same as the
39 * number of censuses that have been performed.
42 * era must be no longer than LDV_SHIFT (15 or 30) bits.
44 * era is initialized to 0 in initHeapProfiling().
46 * max_era is initialized to 2^LDV_SHIFT in initHeapProfiling().
47 * When era reaches max_era, the profiling stops because a closure can
48 * store only up to (max_era - 1) as its creation or last use time.
49 * -------------------------------------------------------------------------- */
53 /* -----------------------------------------------------------------------------
55 -------------------------------------------------------------------------- */
56 typedef struct _counter {
61 int prim; // total size of 'inherently used' closures
62 int unused; // total size of 'never used' closures
63 int used; // total size of 'used at least once' closures
64 int void_new; // current total size of 'destroyed without being used' closures
65 int drag_new; // current total size of 'used at least once and waiting to die'
68 struct _counter *next;
72 double time; // the time in MUT time when the census is made
77 // for LDV profiling, when just displaying by LDV
85 Census *censuses = NULL;
88 /* --------------------------------------------------------------------------
89 * Profiling type predicates
90 * ----------------------------------------------------------------------- */
93 doingLDVProfiling( void )
95 return (RtsFlags.ProfFlags.doHeapProfile == HEAP_BY_LDV
96 || RtsFlags.ProfFlags.bioSelector != NULL);
100 doingRetainerProfiling( void )
102 return (RtsFlags.ProfFlags.doHeapProfile == HEAP_BY_RETAINER
103 || RtsFlags.ProfFlags.retainerSelector != NULL);
107 // Precesses a closure 'c' being destroyed whose size is 'size'.
108 // Make sure that LDV_recordDead() is not invoked on 'inherently used' closures
109 // such as TSO; they should not be involved in computing dragNew or voidNew.
111 // Even though era is checked in both LdvCensusForDead() and
112 // LdvCensusKillAll(), we still need to make sure that era is > 0 because
113 // LDV_recordDead() may be called from elsewhere in the runtime system. E.g.,
114 // when a thunk is replaced by an indirection object.
118 LDV_recordDead( StgClosure *c, nat size )
120 if (era > 0 && closureSatisfiesConstraints(c)) {
122 size -= sizeofW(StgProfHeader);
123 if ((LDVW((c)) & LDV_STATE_MASK) == LDV_STATE_CREATE) {
124 t = (LDVW((c)) & LDV_CREATE_MASK) >> LDV_SHIFT;
126 censuses[t].void_total += (int)size;
127 censuses[era].void_total -= (int)size;
130 t = LDVW((c)) & LDV_LAST_MASK;
132 censuses[t + 1].drag_total += size;
133 censuses[era].drag_total -= size;
140 /* --------------------------------------------------------------------------
141 * Initialize censuses[era];
142 * ----------------------------------------------------------------------- */
146 censuses[era].not_used = 0;
147 censuses[era].used = 0;
148 censuses[era].prim = 0;
149 censuses[era].void_total = 0;
150 censuses[era].drag_total = 0;
153 /* --------------------------------------------------------------------------
154 * Increases era by 1 and initialize census[era].
155 * Reallocates gi[] and increases its size if needed.
156 * ----------------------------------------------------------------------- */
161 if (doingLDVProfiling()) {
164 if (era == max_era) {
165 barf("maximum number of censuses reached; use +RTS -i to reduce");
168 if (era == n_censuses) {
170 censuses = stgReallocBytes(censuses, sizeof(Census) * n_censuses,
179 /* -------------------------------------------------------------------------- */
181 #ifdef DEBUG_HEAP_PROF
184 void initProfiling1( void )
188 void initProfiling2( void )
193 void endProfiling( void )
197 #endif /* DEBUG_HEAP_PROF */
200 initHeapProfiling(void)
202 if (! RtsFlags.ProfFlags.doHeapProfile) {
206 // we only count eras if we're doing LDV profiling. Otherwise era
209 if (doingLDVProfiling()) {
217 { // max_era = 2^LDV_SHIFT
220 for (p = 0; p < LDV_SHIFT; p++)
225 censuses = stgMallocBytes(sizeof(Census) * n_censuses, "initHeapProfiling");
227 fprintf(hp_file, "JOB \"%s", prog_argv[0]);
232 for(count = 1; count < prog_argc; count++)
233 fprintf(hp_file, " %s", prog_argv[count]);
234 fprintf(hp_file, " +RTS ");
235 for(count = 0; count < rts_argc; count++)
236 fprintf(hp_file, "%s ", rts_argv[count]);
237 fprintf(hp_file, "\n");
239 #endif /* PROFILING */
241 fprintf(hp_file, "\"\n" );
243 fprintf(hp_file, "DATE \"%s\"\n", time_str());
245 fprintf(hp_file, "SAMPLE_UNIT \"seconds\"\n");
246 fprintf(hp_file, "VALUE_UNIT \"bytes\"\n");
248 fprintf(hp_file, "BEGIN_SAMPLE 0.00\n");
249 fprintf(hp_file, "END_SAMPLE 0.00\n");
251 #ifdef DEBUG_HEAP_PROF
252 DEBUG_LoadSymbols(prog_argv[0]);
256 if (doingRetainerProfiling()) {
257 initRetainerProfiling();
265 endHeapProfiling(void)
269 if (! RtsFlags.ProfFlags.doHeapProfile) {
274 if (doingRetainerProfiling()) {
275 endRetainerProfiling();
281 // We do not need to perform a major garbage collection because all the
282 // closures created since the last census will not affect the profiling
283 // statistics anyhow.
284 if (RtsFlags.ProfFlags.doHeapProfile == HEAP_BY_LDV)
289 // At last... we can output the census info for LDV profiling
290 if (RtsFlags.ProfFlags.doHeapProfile == HEAP_BY_LDV) {
292 int sumVoidNew, sumDragNew;
294 // Now we compute void_total and drag_total for each census
297 for (t = 1; t < era; t++) { // note: start at 1, not 0
298 sumVoidNew += censuses[t].void_total;
299 sumDragNew += censuses[t].drag_total;
300 censuses[t].void_total = sumVoidNew;
301 censuses[t].drag_total = sumDragNew;
302 ASSERT( censuses[t].void_total < censuses[t].not_used );
303 ASSERT( censuses[t].drag_total < censuses[t].used );
306 for (t = 1; t < era; t++) { // note: start at 1, not 0
307 fprintf(hp_file, "MARK %f\n", censuses[t].time);
308 fprintf(hp_file, "BEGIN_SAMPLE %f\n", censuses[t].time);
309 fprintf(hp_file, "VOID\t%u\n", censuses[t].void_total * sizeof(W_));
310 fprintf(hp_file, "LAG\t%u\n",
311 (censuses[t].not_used - censuses[t].void_total) * sizeof(W_));
312 fprintf(hp_file, "USE\t%u\n",
313 (censuses[t].used - censuses[t].drag_total) * sizeof(W_));
314 fprintf(hp_file, "INHERENT_USE\t%u\n",
315 censuses[t].prim * sizeof(W_));
316 fprintf(hp_file, "DRAG\t%u\n", censuses[t].drag_total * sizeof(W_));
317 fprintf(hp_file, "END_SAMPLE %f\n", censuses[t].time);
322 seconds = mut_user_time();
323 fprintf(hp_file, "BEGIN_SAMPLE %0.2f\n", seconds);
324 fprintf(hp_file, "END_SAMPLE %0.2f\n", seconds);
328 #ifdef DEBUG_HEAP_PROF
329 /* -----------------------------------------------------------------------------
330 Closure Type Profiling;
332 PROBABLY TOTALLY OUT OF DATE -- ToDo (SDM)
333 -------------------------------------------------------------------------- */
335 static char *type_names[] = {
341 , "CONSTR_NOCAF_STATIC"
379 , "MUT_ARR_PTRS_FROZEN"
393 #endif /* DEBUG_HEAP_PROF */
398 fprint_ccs(FILE *fp, CostCentreStack *ccs, nat max_length)
400 char buf[max_length+1];
405 // MAIN on its own gets printed as "MAIN", otherwise we ignore MAIN.
406 if (ccs == CCS_MAIN) {
411 // keep printing components of the stack until we run out of space
412 // in the buffer. If we run out of space, end with "...".
413 for (; ccs != NULL && ccs != CCS_MAIN; ccs = ccs->prevStack) {
415 // CAF cost centres print as M.CAF, but we leave the module
416 // name out of all the others to save space.
417 if (!strcmp(ccs->cc->label,"CAF")) {
418 written = snprintf(buf+next_offset,
419 (int)max_length-3-(int)next_offset,
420 "%s.CAF", ccs->cc->module);
422 if (ccs->prevStack != NULL && ccs->prevStack != CCS_MAIN) {
427 written = snprintf(buf+next_offset,
428 (int)max_length-3-(int)next_offset,
429 template, ccs->cc->label);
432 if (next_offset+written >= max_length-4) {
433 sprintf(buf+max_length-4, "...");
436 next_offset += written;
439 fprintf(fp, "%s", buf);
443 str_matches_selector( char* str, char* sel )
446 // fprintf(stderr, "str_matches_selector %s %s\n", str, sel);
448 // Compare str against wherever we've got to in sel.
450 while (*p != '\0' && *sel != ',' && *sel != '\0' && *p == *sel) {
453 // Match if all of str used and have reached the end of a sel fragment.
454 if (*p == '\0' && (*sel == ',' || *sel == '\0'))
457 // No match. Advance sel to the start of the next elem.
458 while (*sel != ',' && *sel != '\0') sel++;
459 if (*sel == ',') sel++;
461 /* Run out of sel ?? */
462 if (*sel == '\0') return rtsFalse;
466 // Figure out whether a closure should be counted in this census, by
467 // testing against all the specified constraints.
469 closureSatisfiesConstraints( StgClosure* p )
472 if (RtsFlags.ProfFlags.modSelector) {
473 b = str_matches_selector( ((StgClosure *)p)->header.prof.ccs->cc->module,
474 RtsFlags.ProfFlags.modSelector );
475 if (!b) return rtsFalse;
477 if (RtsFlags.ProfFlags.descrSelector) {
478 b = str_matches_selector( (get_itbl((StgClosure *)p))->prof.closure_desc,
479 RtsFlags.ProfFlags.descrSelector );
480 if (!b) return rtsFalse;
482 if (RtsFlags.ProfFlags.typeSelector) {
483 b = str_matches_selector( (get_itbl((StgClosure *)p))->prof.closure_type,
484 RtsFlags.ProfFlags.typeSelector );
485 if (!b) return rtsFalse;
487 if (RtsFlags.ProfFlags.ccSelector) {
488 b = str_matches_selector( ((StgClosure *)p)->header.prof.ccs->cc->label,
489 RtsFlags.ProfFlags.ccSelector );
490 if (!b) return rtsFalse;
492 if (RtsFlags.ProfFlags.retainerSelector) {
495 rs = retainerSetOf((StgClosure *)p);
497 for (i = 0; i < rs->num; i++) {
498 b = str_matches_selector( rs->element[i]->cc->label,
499 RtsFlags.ProfFlags.retainerSelector );
500 if (b) return rtsTrue;
507 #endif /* PROFILING */
509 /* -----------------------------------------------------------------------------
510 * Print out the results of a heap census.
511 * -------------------------------------------------------------------------- */
513 dumpCensus( Census *census )
518 // We can't generate any info for LDV profiling until
519 // the end of the run...
520 if (doingLDVProfiling()) { return; }
523 fprintf(hp_file, "BEGIN_SAMPLE %0.2f\n", census->time);
525 for (ctr = census->ctrs; ctr != NULL; ctr = ctr->next) {
527 #ifdef DEBUG_HEAP_PROF
528 switch (RtsFlags.ProfFlags.doHeapProfile) {
529 case HEAP_BY_INFOPTR:
530 fprint_data(hp_file);
532 case HEAP_BY_CLOSURE_TYPE:
533 fprint_closure_types(hp_file);
539 switch (RtsFlags.ProfFlags.doHeapProfile) {
541 fprint_ccs(hp_file, (CostCentreStack *)ctr->identity, 30);
546 fprintf(hp_file, "%s", (char *)ctr->identity);
548 case HEAP_BY_RETAINER:
550 RetainerSet *rs = (RetainerSet *)ctr->identity;
552 // Mark this retainer set by negating its id, because it
553 // has appeared in at least one census. We print the
554 // values of all such retainer sets into the log file at
555 // the end. A retainer set may exist but not feature in
556 // any censuses if it arose as the intermediate retainer
557 // set for some closure during retainer set calculation.
561 // report in the unit of bytes: * sizeof(StgWord)
562 printRetainerSetShort(hp_file, rs);
566 barf("dumpCensus; doHeapProfile");
570 fprintf(hp_file, "\t%d\n", ctr->c.resid * sizeof(W_));
573 fprintf(hp_file, "END_SAMPLE %0.2f\n", census->time);
576 /* -----------------------------------------------------------------------------
577 * Code to perform a heap census.
578 * -------------------------------------------------------------------------- */
580 heapCensusChain( Census *census, bdescr *bd )
590 for (; bd != NULL; bd = bd->link) {
592 while (p < bd->free) {
593 info = get_itbl((StgClosure *)p);
596 switch (info->type) {
602 case IND_OLDGEN_PERM:
604 case SE_CAF_BLACKHOLE:
609 case CONSTR_CHARLIKE:
623 size = sizeW_fromITBL(info);
634 size = sizeW_fromITBL(info);
637 case THUNK_1_0: /* ToDo - shouldn't be here */
638 case THUNK_0_1: /* " ditto " */
640 size = sizeofW(StgHeader) + MIN_UPD_SIZE;
645 size = pap_sizeW((StgPAP *)p);
650 size = arr_words_sizeW(stgCast(StgArrWords*,p));
654 case MUT_ARR_PTRS_FROZEN:
656 size = mut_arr_ptrs_sizeW((StgMutArrPtrs *)p);
661 size = tso_sizeW((StgTSO *)p);
670 #ifdef DEBUG_HEAP_PROF
672 switch (RtsFlags.ProfFlags.doHeapProfile) {
673 case HEAP_BY_INFOPTR:
674 identity = (void *)((StgClosure *)p)->header.info;
676 case HEAP_BY_CLOSURE_TYPE:
677 identity = type_names[info->type];
680 barf("heapCensus; doHeapProfile");
685 // subtract the profiling overhead
686 real_size = size - sizeofW(StgProfHeader);
688 if (closureSatisfiesConstraints((StgClosure*)p)) {
689 switch (RtsFlags.ProfFlags.doHeapProfile) {
691 identity = ((StgClosure *)p)->header.prof.ccs;
694 identity = ((StgClosure *)p)->header.prof.ccs->cc->module;
697 identity = (get_itbl((StgClosure *)p))->prof.closure_desc;
700 identity = (get_itbl((StgClosure *)p))->prof.closure_type;
702 case HEAP_BY_RETAINER:
703 identity = retainerSetOf((StgClosure *)p);
707 census->prim += real_size;
708 else if ((LDVW(p) & LDV_STATE_MASK) == LDV_STATE_CREATE)
709 census->not_used += real_size;
711 census->used += real_size;
712 // NOTE: don't break here. We're not using the
717 barf("heapCensus; doHeapProfile");
722 if (identity != NULL) {
723 ctr = lookupHashTable( census->hash, (StgWord)identity );
725 ctr->c.resid += real_size;
727 ctr = arenaAlloc( census->arena, sizeof(counter) );
728 insertHashTable( census->hash, (StgWord)identity, ctr );
729 ctr->c.resid = real_size;
730 ctr->identity = identity;
731 ctr->next = census->ctrs;
747 stat_startHeapCensus();
749 census = &censuses[era];
750 census->time = mut_user_time();
751 census->hash = allocHashTable();
753 census->arena = newArena();
755 // calculate retainer sets if necessary
757 if (doingRetainerProfiling()) {
762 // traverse the heap, collecting the census info
763 heapCensusChain( census, small_alloc_list );
764 if (RtsFlags.GcFlags.generations == 1) {
765 heapCensusChain( census, g0s0->to_blocks );
767 for (g = 0; g < RtsFlags.GcFlags.generations; g++) {
768 for (s = 0; s < generations[g].n_steps; s++) {
769 heapCensusChain( census, generations[g].steps[s].blocks );
770 // Are we interested in large objects? might be
771 // confusing to include the stack in a heap profile.
772 // heapCensusChain( census, generations[g].steps[s].large_objects );
777 // dump out the census info
778 dumpCensus( census );
781 freeHashTable(census->hash, NULL/* don't free the elements */);
782 arenaFree(census->arena);
784 // we're into the next time period now
787 stat_endHeapCensus();
790 #endif /* PROFILING || DEBUG_HEAP_PROF */