[project @ 2002-05-07 04:58:15 by sof]
[ghc-hetmet.git] / ghc / rts / ProfHeap.c
1 /* -----------------------------------------------------------------------------
2  * $Id: ProfHeap.c,v 1.36 2002/05/07 04:58:15 sof Exp $
3  *
4  * (c) The GHC Team, 1998-2000
5  *
6  * Support for heap profiling
7  *
8  * ---------------------------------------------------------------------------*/
9
10 #if defined(DEBUG) && !defined(PROFILING)
11 #define DEBUG_HEAP_PROF
12 #else
13 #undef DEBUG_HEAP_PROF
14 #endif
15
16 #if defined(PROFILING) || defined(DEBUG_HEAP_PROF)
17
18 #include "PosixSource.h"
19 #include "Rts.h"
20 #include "RtsUtils.h"
21 #include "RtsFlags.h"
22 #include "Profiling.h"
23 #include "Storage.h"
24 #include "ProfHeap.h"
25 #include "Stats.h"
26 #include "Hash.h"
27 #include "StrHash.h"
28 #include "RetainerProfile.h"
29 #include "LdvProfile.h"
30 #include "Arena.h"
31 #include "Printer.h"
32
33 /* -----------------------------------------------------------------------------
34  * era stores the current time period.  It is the same as the
35  * number of censuses that have been performed.
36  *
37  * RESTRICTION:
38  *   era must be no longer than LDV_SHIFT (15 or 30) bits.
39  * Invariants:
40  *   era is initialized to 1 in initHeapProfiling().
41  *
42  * max_era is initialized to 2^LDV_SHIFT in initHeapProfiling().
43  * When era reaches max_era, the profiling stops because a closure can
44  * store only up to (max_era - 1) as its creation or last use time.
45  * -------------------------------------------------------------------------- */
46 nat era;
47 static nat max_era;
48
49 /* -----------------------------------------------------------------------------
50  * Counters
51  *
52  * For most heap profiles each closure identity gets a simple count
53  * of live words in the heap at each census.  However, if we're
54  * selecting by biography, then we have to keep the various
55  * lag/drag/void counters for each identity.
56  * -------------------------------------------------------------------------- */
57 typedef struct _counter {
58     void *identity;
59     union {
60         nat resid;
61         struct {
62             int prim;     // total size of 'inherently used' closures
63             int not_used; // total size of 'never used' closures
64             int used;     // total size of 'used at least once' closures
65             int void_total;  // current total size of 'destroyed without being used' closures
66             int drag_total;  // current total size of 'used at least once and waiting to die'
67         } ldv;
68     } c;
69     struct _counter *next;
70 } counter;
71
72 static inline void
73 initLDVCtr( counter *ctr )
74 {
75     ctr->c.ldv.prim = 0;
76     ctr->c.ldv.not_used = 0;
77     ctr->c.ldv.used = 0;
78     ctr->c.ldv.void_total = 0;
79     ctr->c.ldv.drag_total = 0;
80 }
81
82 typedef struct {
83     double      time;    // the time in MUT time when the census is made
84     HashTable * hash;
85     counter   * ctrs;
86     Arena     * arena;
87
88     // for LDV profiling, when just displaying by LDV
89     int       prim;
90     int       not_used;
91     int       used;
92     int       void_total;
93     int       drag_total;
94 } Census;
95
96 Census *censuses = NULL;
97 nat n_censuses = 0;
98
99 #ifdef PROFILING
100 static void aggregateCensusInfo( void );
101 #endif
102
103 static void dumpCensus( Census *census );
104
105 /* -----------------------------------------------------------------------------
106    Closure Type Profiling;
107
108    PROBABLY TOTALLY OUT OF DATE -- ToDo (SDM)
109    -------------------------------------------------------------------------- */
110
111 #ifdef DEBUG_HEAP_PROF
112 static char *type_names[] = {
113       "INVALID_OBJECT"
114     , "CONSTR"
115     , "CONSTR_INTLIKE"
116     , "CONSTR_CHARLIKE"
117     , "CONSTR_STATIC"
118     , "CONSTR_NOCAF_STATIC"
119
120     , "FUN"
121     , "FUN_STATIC"
122
123     , "THUNK"
124     , "THUNK_STATIC"
125     , "THUNK_SELECTOR"
126
127     , "BCO"
128     , "AP_UPD"
129
130     , "PAP"
131
132     , "IND"
133     , "IND_OLDGEN"
134     , "IND_PERM"
135     , "IND_OLDGEN_PERM"
136     , "IND_STATIC"
137
138     , "RET_BCO"
139     , "RET_SMALL"
140     , "RET_VEC_SMALL"
141     , "RET_BIG"
142     , "RET_VEC_BIG"
143     , "RET_DYN"
144     , "UPDATE_FRAME"
145     , "CATCH_FRAME"
146     , "STOP_FRAME"
147     , "SEQ_FRAME"
148
149     , "BLACKHOLE"
150     , "BLACKHOLE_BQ"
151     , "MVAR"
152
153     , "ARR_WORDS"
154
155     , "MUT_ARR_PTRS"
156     , "MUT_ARR_PTRS_FROZEN"
157     , "MUT_VAR"
158
159     , "WEAK"
160     , "FOREIGN"
161   
162     , "TSO"
163
164     , "BLOCKED_FETCH"
165     , "FETCH_ME"
166
167     , "EVACUATED"
168 };
169
170 #endif /* DEBUG_HEAP_PROF */
171
172 /* -----------------------------------------------------------------------------
173  * Find the "closure identity", which is a unique pointer reresenting
174  * the band to which this closure's heap space is attributed in the
175  * heap profile.
176  * ------------------------------------------------------------------------- */
177 static inline void *
178 closureIdentity( StgClosure *p )
179 {
180     switch (RtsFlags.ProfFlags.doHeapProfile) {
181
182 #ifdef PROFILING
183     case HEAP_BY_CCS:
184         return p->header.prof.ccs;
185     case HEAP_BY_MOD:
186         return p->header.prof.ccs->cc->module;
187     case HEAP_BY_DESCR:
188         return get_itbl(p)->prof.closure_desc;
189     case HEAP_BY_TYPE:
190         return get_itbl(p)->prof.closure_type;
191     case HEAP_BY_RETAINER:
192         // AFAIK, the only closures in the heap which might not have a
193         // valid retainer set are DEAD_WEAK closures.
194         if (isRetainerSetFieldValid(p))
195             return retainerSetOf(p);
196         else
197             return NULL;
198
199 #else // DEBUG
200     case HEAP_BY_INFOPTR:
201         return (void *)((StgClosure *)p)->header.info; 
202     case HEAP_BY_CLOSURE_TYPE:
203         return type_names[get_itbl(p)->type];
204
205 #endif
206     default:
207         barf("closureIdentity");
208     }
209 }
210
211 /* --------------------------------------------------------------------------
212  * Profiling type predicates
213  * ----------------------------------------------------------------------- */
214 #ifdef PROFILING
215 static inline rtsBool
216 doingLDVProfiling( void )
217 {
218     return (RtsFlags.ProfFlags.doHeapProfile == HEAP_BY_LDV 
219             || RtsFlags.ProfFlags.bioSelector != NULL);
220 }
221
222 static inline rtsBool
223 doingRetainerProfiling( void )
224 {
225     return (RtsFlags.ProfFlags.doHeapProfile == HEAP_BY_RETAINER
226             || RtsFlags.ProfFlags.retainerSelector != NULL);
227 }
228 #endif // PROFILING
229
230 // Precesses a closure 'c' being destroyed whose size is 'size'.
231 // Make sure that LDV_recordDead() is not invoked on 'inherently used' closures
232 // such as TSO; they should not be involved in computing dragNew or voidNew.
233 // 
234 // Even though era is checked in both LdvCensusForDead() and 
235 // LdvCensusKillAll(), we still need to make sure that era is > 0 because 
236 // LDV_recordDead() may be called from elsewhere in the runtime system. E.g., 
237 // when a thunk is replaced by an indirection object.
238
239 #ifdef PROFILING
240 void
241 LDV_recordDead( StgClosure *c, nat size )
242 {
243     void *id;
244     nat t;
245     counter *ctr;
246
247     if (era > 0 && closureSatisfiesConstraints(c)) {
248         size -= sizeofW(StgProfHeader);
249         if ((LDVW((c)) & LDV_STATE_MASK) == LDV_STATE_CREATE) {
250             t = (LDVW((c)) & LDV_CREATE_MASK) >> LDV_SHIFT;
251             if (t < era) {
252                 if (RtsFlags.ProfFlags.bioSelector == NULL) {
253                     censuses[t].void_total   += (int)size;
254                     censuses[era].void_total -= (int)size;
255                 } else {
256                     id = closureIdentity(c);
257                     ctr = lookupHashTable(censuses[t].hash, (StgWord)id);
258                     ASSERT( ctr != NULL );
259                     ctr->c.ldv.void_total += (int)size;
260                     ctr = lookupHashTable(censuses[era].hash, (StgWord)id);
261                     if (ctr == NULL) {
262                         ctr = arenaAlloc(censuses[era].arena, sizeof(counter));
263                         initLDVCtr(ctr);
264                         insertHashTable(censuses[era].hash, (StgWord)id, ctr);
265                         ctr->identity = id;
266                         ctr->next = censuses[era].ctrs;
267                         censuses[era].ctrs = ctr;
268                     }
269                     ctr->c.ldv.void_total -= (int)size;
270                 }
271             }
272         } else {
273             t = LDVW((c)) & LDV_LAST_MASK;
274             if (t + 1 < era) {
275                 if (RtsFlags.ProfFlags.bioSelector == NULL) {
276                     censuses[t+1].drag_total += size;
277                     censuses[era].drag_total -= size;
278                 } else {
279                     void *id;
280                     id = closureIdentity(c);
281                     ctr = lookupHashTable(censuses[t+1].hash, (StgWord)id);
282                     ASSERT( ctr != NULL );
283                     ctr->c.ldv.drag_total += (int)size;
284                     ctr = lookupHashTable(censuses[era].hash, (StgWord)id);
285                     if (ctr == NULL) {
286                         ctr = arenaAlloc(censuses[era].arena, sizeof(counter));
287                         initLDVCtr(ctr);
288                         insertHashTable(censuses[era].hash, (StgWord)id, ctr);
289                         ctr->identity = id;
290                         ctr->next = censuses[era].ctrs;
291                         censuses[era].ctrs = ctr;
292                     }
293                     ctr->c.ldv.drag_total -= (int)size;
294                 }
295             }
296         }
297     }
298 }
299 #endif
300
301 /* --------------------------------------------------------------------------
302  * Initialize censuses[era];
303  * ----------------------------------------------------------------------- */
304 static inline void
305 initEra(Census *census)
306 {
307     census->hash  = allocHashTable();
308     census->ctrs  = NULL;
309     census->arena = newArena();
310
311     census->not_used   = 0;
312     census->used       = 0;
313     census->prim       = 0;
314     census->void_total = 0;
315     census->drag_total = 0;
316 }
317
318 /* --------------------------------------------------------------------------
319  * Increases era by 1 and initialize census[era].
320  * Reallocates gi[] and increases its size if needed.
321  * ----------------------------------------------------------------------- */
322 static void
323 nextEra( void )
324 {
325 #ifdef PROFILING
326     if (doingLDVProfiling()) { 
327         era++;
328
329         if (era == max_era) {
330             barf("maximum number of censuses reached; use +RTS -i to reduce");
331         }
332         
333         if (era == n_censuses) {
334             n_censuses *= 2;
335             censuses = stgReallocBytes(censuses, sizeof(Census) * n_censuses,
336                                        "nextEra");
337         }
338     }
339 #endif // PROFILING
340         
341     initEra( &censuses[era] );
342 }
343
344 /* -----------------------------------------------------------------------------
345  * DEBUG heap profiling, by info table
346  * -------------------------------------------------------------------------- */
347
348 #ifdef DEBUG_HEAP_PROF
349 FILE *hp_file;
350
351 void initProfiling1( void )
352 {
353 }
354
355 void initProfiling2( void )
356 {
357   initHeapProfiling();
358 }
359
360 void endProfiling( void )
361 {
362   endHeapProfiling();
363 }
364 #endif /* DEBUG_HEAP_PROF */
365
366 /* --------------------------------------------------------------------------
367  * Initialize the heap profilier
368  * ----------------------------------------------------------------------- */
369 nat
370 initHeapProfiling(void)
371 {
372     if (! RtsFlags.ProfFlags.doHeapProfile) {
373         return 0;
374     }
375
376 #ifdef PROFILING
377     if (doingLDVProfiling() && doingRetainerProfiling()) {
378         prog_belch("cannot mix -hb and -hr");
379         stg_exit(1);
380     }
381 #endif
382
383     // we only count eras if we're doing LDV profiling.  Otherwise era
384     // is fixed at zero.
385 #ifdef PROFILING
386     if (doingLDVProfiling()) {
387         era = 1;
388     } else
389 #endif
390     {
391         era = 0;
392     }
393
394     {   // max_era = 2^LDV_SHIFT
395         nat p;
396         max_era = 1;
397         for (p = 0; p < LDV_SHIFT; p++)
398             max_era *= 2;
399     }
400
401     n_censuses = 32;
402     censuses = stgMallocBytes(sizeof(Census) * n_censuses, "initHeapProfiling");
403
404     initEra( &censuses[era] );
405
406     fprintf(hp_file, "JOB \"%s", prog_argv[0]);
407
408 #ifdef PROFILING
409     {
410         int count;
411         for(count = 1; count < prog_argc; count++)
412             fprintf(hp_file, " %s", prog_argv[count]);
413         fprintf(hp_file, " +RTS ");
414         for(count = 0; count < rts_argc; count++)
415             fprintf(hp_file, "%s ", rts_argv[count]);
416         fprintf(hp_file, "\n");
417     }
418 #endif /* PROFILING */
419
420     fprintf(hp_file, "\"\n" );
421
422     fprintf(hp_file, "DATE \"%s\"\n", time_str());
423
424     fprintf(hp_file, "SAMPLE_UNIT \"seconds\"\n");
425     fprintf(hp_file, "VALUE_UNIT \"bytes\"\n");
426
427     fprintf(hp_file, "BEGIN_SAMPLE 0.00\n");
428     fprintf(hp_file, "END_SAMPLE 0.00\n");
429
430 #ifdef DEBUG_HEAP_PROF
431     DEBUG_LoadSymbols(prog_argv[0]);
432 #endif
433
434 #ifdef PROFILING
435     if (doingRetainerProfiling()) {
436         initRetainerProfiling();
437     }
438 #endif
439
440     return 0;
441 }
442
443 void
444 endHeapProfiling(void)
445 {
446     StgDouble seconds;
447
448     if (! RtsFlags.ProfFlags.doHeapProfile) {
449         return;
450     }
451
452 #ifdef PROFILING
453     if (doingRetainerProfiling()) {
454         endRetainerProfiling();
455     }
456 #endif
457
458 #ifdef PROFILING
459     if (doingLDVProfiling()) {
460         nat t;
461         LdvCensusKillAll();
462         aggregateCensusInfo();
463         for (t = 1; t < era; t++) {
464             dumpCensus( &censuses[t] );
465         }
466     }
467 #endif
468
469     seconds = mut_user_time();
470     fprintf(hp_file, "BEGIN_SAMPLE %0.2f\n", seconds);
471     fprintf(hp_file, "END_SAMPLE %0.2f\n", seconds);
472     fclose(hp_file);
473 }
474
475
476
477 #ifdef PROFILING
478 static void
479 fprint_ccs(FILE *fp, CostCentreStack *ccs, nat max_length)
480 {
481     char buf[max_length+1];
482     nat next_offset = 0;
483     nat written;
484     char *template;
485
486     // MAIN on its own gets printed as "MAIN", otherwise we ignore MAIN.
487     if (ccs == CCS_MAIN) {
488         fprintf(fp, "MAIN");
489         return;
490     }
491
492     fprintf(fp, "(%d)", ccs->ccsID);
493
494     // keep printing components of the stack until we run out of space
495     // in the buffer.  If we run out of space, end with "...".
496     for (; ccs != NULL && ccs != CCS_MAIN; ccs = ccs->prevStack) {
497
498         // CAF cost centres print as M.CAF, but we leave the module
499         // name out of all the others to save space.
500         if (!strcmp(ccs->cc->label,"CAF")) {
501 #ifdef HAVE_SNPRINTF
502             written = snprintf(buf+next_offset, 
503                                (int)max_length-3-(int)next_offset,
504                                "%s.CAF", ccs->cc->module);
505 #else
506             written = sprintf(buf+next_offset, 
507                                "%s.CAF", ccs->cc->module);
508 #endif
509         } else {
510             if (ccs->prevStack != NULL && ccs->prevStack != CCS_MAIN) {
511                 template = "%s/";
512             } else {
513                 template = "%s";
514             }
515 #ifdef HAVE_SNPRINTF
516             written = snprintf(buf+next_offset, 
517                                (int)max_length-3-(int)next_offset,
518                                template, ccs->cc->label);
519 #else
520             written = sprintf(buf+next_offset, 
521                                template, ccs->cc->label);
522 #endif
523         }
524
525         if (next_offset+written >= max_length-4) {
526             sprintf(buf+max_length-4, "...");
527             break;
528         } else {
529             next_offset += written;
530         }
531     }
532     fprintf(fp, "%s", buf);
533 }
534 #endif // PROFILING
535
536 rtsBool
537 strMatchesSelector( char* str, char* sel )
538 {
539    char* p;
540    // fprintf(stderr, "str_matches_selector %s %s\n", str, sel);
541    while (1) {
542        // Compare str against wherever we've got to in sel.
543        p = str;
544        while (*p != '\0' && *sel != ',' && *sel != '\0' && *p == *sel) {
545            p++; sel++;
546        }
547        // Match if all of str used and have reached the end of a sel fragment.
548        if (*p == '\0' && (*sel == ',' || *sel == '\0'))
549            return rtsTrue;
550        
551        // No match.  Advance sel to the start of the next elem.
552        while (*sel != ',' && *sel != '\0') sel++;
553        if (*sel == ',') sel++;
554        
555        /* Run out of sel ?? */
556        if (*sel == '\0') return rtsFalse;
557    }
558 }
559
560 /* -----------------------------------------------------------------------------
561  * Figure out whether a closure should be counted in this census, by
562  * testing against all the specified constraints.
563  * -------------------------------------------------------------------------- */
564 rtsBool
565 closureSatisfiesConstraints( StgClosure* p )
566 {
567 #ifdef DEBUG_HEAP_PROF
568     return rtsTrue;
569 #else
570    rtsBool b;
571
572    // The CCS has a selected field to indicate whether this closure is
573    // deselected by not being mentioned in the module, CC, or CCS
574    // selectors.
575    if (!p->header.prof.ccs->selected) {
576        return rtsFalse;
577    }
578
579    if (RtsFlags.ProfFlags.descrSelector) {
580        b = strMatchesSelector( (get_itbl((StgClosure *)p))->prof.closure_desc,
581                                  RtsFlags.ProfFlags.descrSelector );
582        if (!b) return rtsFalse;
583    }
584    if (RtsFlags.ProfFlags.typeSelector) {
585        b = strMatchesSelector( (get_itbl((StgClosure *)p))->prof.closure_type,
586                                 RtsFlags.ProfFlags.typeSelector );
587        if (!b) return rtsFalse;
588    }
589    if (RtsFlags.ProfFlags.retainerSelector) {
590        RetainerSet *rs;
591        nat i;
592        rs = retainerSetOf((StgClosure *)p);
593        if (rs != NULL) {
594            for (i = 0; i < rs->num; i++) {
595                b = strMatchesSelector( rs->element[i]->cc->label,
596                                          RtsFlags.ProfFlags.retainerSelector );
597                if (b) return rtsTrue;
598            }
599        }
600        return rtsFalse;
601    }
602    return rtsTrue;
603 #endif /* PROFILING */
604 }
605
606 /* -----------------------------------------------------------------------------
607  * Aggregate the heap census info for biographical profiling
608  * -------------------------------------------------------------------------- */
609 #ifdef PROFILING
610 static void
611 aggregateCensusInfo( void )
612 {
613     HashTable *acc;
614     nat t;
615     counter *c, *d, *ctrs;
616     Arena *arena;
617
618     if (!doingLDVProfiling()) return;
619
620     // Aggregate the LDV counters when displaying by biography.
621     if (RtsFlags.ProfFlags.doHeapProfile == HEAP_BY_LDV) {
622         int void_total, drag_total;
623
624         // Now we compute void_total and drag_total for each census
625         void_total = 0;
626         drag_total = 0;
627         for (t = 1; t < era; t++) { // note: start at 1, not 0
628             void_total += censuses[t].void_total;
629             drag_total += censuses[t].drag_total;
630             censuses[t].void_total = void_total;
631             censuses[t].drag_total = drag_total;
632             ASSERT( censuses[t].void_total <= censuses[t].not_used );
633             ASSERT( censuses[t].drag_total <= censuses[t].used );
634         }
635         
636         return;
637     }
638
639     // otherwise... we're doing a heap profile that is restricted to
640     // some combination of lag, drag, void or use.  We've kept all the
641     // census info for all censuses so far, but we still need to
642     // aggregate the counters forwards.
643
644     arena = newArena();
645     acc = allocHashTable();
646     ctrs = NULL;
647
648     for (t = 1; t < era; t++) {
649
650         // first look through all the counters we're aggregating
651         for (c = ctrs; c != NULL; c = c->next) {
652             // if one of the totals is non-zero, then this closure
653             // type must be present in the heap at this census time...
654             d = lookupHashTable(censuses[t].hash, (StgWord)c->identity);
655
656             if (d == NULL) {
657                 // if this closure identity isn't present in the
658                 // census for this time period, then our running
659                 // totals *must* be zero.
660                 ASSERT(c->c.ldv.void_total == 0 && c->c.ldv.drag_total == 0);
661
662                 // fprintCCS(stderr,c->identity);
663                 // fprintf(stderr," census=%d void_total=%d drag_total=%d\n",
664                 //         t, c->c.ldv.void_total, c->c.ldv.drag_total);
665             } else {
666                 d->c.ldv.void_total += c->c.ldv.void_total;
667                 d->c.ldv.drag_total += c->c.ldv.drag_total;
668                 c->c.ldv.void_total =  d->c.ldv.void_total;
669                 c->c.ldv.drag_total =  d->c.ldv.drag_total;
670
671                 ASSERT( c->c.ldv.void_total >= 0 );
672                 ASSERT( c->c.ldv.drag_total >= 0 );
673             }
674         }
675
676         // now look through the counters in this census to find new ones
677         for (c = censuses[t].ctrs; c != NULL; c = c->next) {
678             d = lookupHashTable(acc, (StgWord)c->identity);
679             if (d == NULL) {
680                 d = arenaAlloc( arena, sizeof(counter) );
681                 initLDVCtr(d);
682                 insertHashTable( acc, (StgWord)c->identity, d );
683                 d->identity = c->identity;
684                 d->next = ctrs;
685                 ctrs = d;
686                 d->c.ldv.void_total = c->c.ldv.void_total;
687                 d->c.ldv.drag_total = c->c.ldv.drag_total;
688             }
689             ASSERT( c->c.ldv.void_total >= 0 );
690             ASSERT( c->c.ldv.drag_total >= 0 );
691         }
692     }
693
694     freeHashTable(acc, NULL);
695     arenaFree(arena);
696 }
697 #endif
698
699 /* -----------------------------------------------------------------------------
700  * Print out the results of a heap census.
701  * -------------------------------------------------------------------------- */
702 static void
703 dumpCensus( Census *census )
704 {
705     counter *ctr;
706     int count;
707
708     fprintf(hp_file, "BEGIN_SAMPLE %0.2f\n", census->time);
709
710 #ifdef PROFILING
711     if (RtsFlags.ProfFlags.doHeapProfile == HEAP_BY_LDV) {
712         fprintf(hp_file, "VOID\t%u\n", census->void_total * sizeof(W_));
713         fprintf(hp_file, "LAG\t%u\n", 
714                 (census->not_used - census->void_total) * sizeof(W_));
715         fprintf(hp_file, "USE\t%u\n", 
716                 (census->used - census->drag_total) * sizeof(W_));
717         fprintf(hp_file, "INHERENT_USE\t%u\n", 
718                 census->prim * sizeof(W_));
719         fprintf(hp_file, "DRAG\t%u\n", census->drag_total *
720                 sizeof(W_));
721         fprintf(hp_file, "END_SAMPLE %0.2f\n", census->time);
722         return;
723     }
724 #endif
725
726     for (ctr = census->ctrs; ctr != NULL; ctr = ctr->next) {
727
728 #ifdef PROFILING
729         if (RtsFlags.ProfFlags.bioSelector != NULL) {
730             count = 0;
731             if (strMatchesSelector("lag", RtsFlags.ProfFlags.bioSelector))
732                 count += ctr->c.ldv.not_used - ctr->c.ldv.void_total;
733             if (strMatchesSelector("drag", RtsFlags.ProfFlags.bioSelector))
734                 count += ctr->c.ldv.drag_total;
735             if (strMatchesSelector("void", RtsFlags.ProfFlags.bioSelector))
736                 count += ctr->c.ldv.void_total;
737             if (strMatchesSelector("use", RtsFlags.ProfFlags.bioSelector))
738                 count += ctr->c.ldv.used - ctr->c.ldv.drag_total;
739         } else
740 #endif
741         {
742             count = ctr->c.resid;
743         }
744
745         ASSERT( count >= 0 );
746
747         if (count == 0) continue;
748
749 #ifdef DEBUG_HEAP_PROF
750         switch (RtsFlags.ProfFlags.doHeapProfile) {
751         case HEAP_BY_INFOPTR:
752             fprintf(hp_file, "%s", lookupGHCName(ctr->identity));
753             break;
754         case HEAP_BY_CLOSURE_TYPE:
755             fprintf(hp_file, "%s", (char *)ctr->identity);
756             break;
757         }
758 #endif
759         
760 #ifdef PROFILING
761         switch (RtsFlags.ProfFlags.doHeapProfile) {
762         case HEAP_BY_CCS:
763             fprint_ccs(hp_file, (CostCentreStack *)ctr->identity, 25);
764             break;
765         case HEAP_BY_MOD:
766         case HEAP_BY_DESCR:
767         case HEAP_BY_TYPE:
768             fprintf(hp_file, "%s", (char *)ctr->identity);
769             break;
770         case HEAP_BY_RETAINER:
771         {
772             RetainerSet *rs = (RetainerSet *)ctr->identity;
773
774             // it might be the distinguished retainer set rs_MANY:
775             if (rs == &rs_MANY) {
776                 fprintf(hp_file, "MANY");
777                 break;
778             }
779
780             // Mark this retainer set by negating its id, because it
781             // has appeared in at least one census.  We print the
782             // values of all such retainer sets into the log file at
783             // the end.  A retainer set may exist but not feature in
784             // any censuses if it arose as the intermediate retainer
785             // set for some closure during retainer set calculation.
786             if (rs->id > 0)
787                 rs->id = -(rs->id);
788
789             // report in the unit of bytes: * sizeof(StgWord)
790             printRetainerSetShort(hp_file, rs);
791             break;
792         }
793         default:
794             barf("dumpCensus; doHeapProfile");
795         }
796 #endif
797
798         fprintf(hp_file, "\t%d\n", count * sizeof(W_));
799     }
800
801     fprintf(hp_file, "END_SAMPLE %0.2f\n", census->time);
802 }
803
804 /* -----------------------------------------------------------------------------
805  * Code to perform a heap census.
806  * -------------------------------------------------------------------------- */
807 static void
808 heapCensusChain( Census *census, bdescr *bd )
809 {
810     StgPtr p;
811     StgInfoTable *info;
812     void *identity;
813     nat size;
814     counter *ctr;
815     nat real_size;
816     rtsBool prim;
817
818     for (; bd != NULL; bd = bd->link) {
819         p = bd->start;
820         while (p < bd->free) {
821             info = get_itbl((StgClosure *)p);
822             prim = rtsFalse;
823             
824             switch (info->type) {
825
826             case CONSTR:
827             case FUN:
828             case THUNK:
829             case IND_PERM:
830             case IND_OLDGEN_PERM:
831             case CAF_BLACKHOLE:
832             case SE_CAF_BLACKHOLE:
833             case SE_BLACKHOLE:
834             case BLACKHOLE:
835             case BLACKHOLE_BQ:
836             case CONSTR_INTLIKE:
837             case CONSTR_CHARLIKE:
838             case FUN_1_0:
839             case FUN_0_1:
840             case FUN_1_1:
841             case FUN_0_2:
842             case FUN_2_0:
843             case THUNK_1_1:
844             case THUNK_0_2:
845             case THUNK_2_0:
846             case CONSTR_1_0:
847             case CONSTR_0_1:
848             case CONSTR_1_1:
849             case CONSTR_0_2:
850             case CONSTR_2_0:
851                 size = sizeW_fromITBL(info);
852                 break;
853                 
854             case BCO:
855             case MVAR:
856             case WEAK:
857             case FOREIGN:
858             case STABLE_NAME:
859             case MUT_VAR:
860             case MUT_CONS:
861                 prim = rtsTrue;
862                 size = sizeW_fromITBL(info);
863                 break;
864
865             case THUNK_1_0:             /* ToDo - shouldn't be here */
866             case THUNK_0_1:             /* "  ditto  " */
867             case THUNK_SELECTOR:
868                 size = sizeofW(StgHeader) + MIN_UPD_SIZE;
869                 break;
870
871             case PAP:
872             case AP_UPD:
873                 size = pap_sizeW((StgPAP *)p);
874                 break;
875                 
876             case ARR_WORDS:
877                 prim = rtsTrue;
878                 size = arr_words_sizeW(stgCast(StgArrWords*,p));
879                 break;
880                 
881             case MUT_ARR_PTRS:
882             case MUT_ARR_PTRS_FROZEN:
883                 prim = rtsTrue;
884                 size = mut_arr_ptrs_sizeW((StgMutArrPtrs *)p);
885                 break;
886                 
887             case TSO:
888                 prim = rtsTrue;
889                 size = tso_sizeW((StgTSO *)p);
890                 break;
891                 
892             default:
893                 barf("heapCensus");
894             }
895             
896             identity = NULL;
897
898 #ifdef DEBUG_HEAP_PROF
899             real_size = size;
900 #else
901             // subtract the profiling overhead
902             real_size = size - sizeofW(StgProfHeader);
903 #endif
904
905             if (closureSatisfiesConstraints((StgClosure*)p)) {
906 #ifdef PROFILING
907                 if (RtsFlags.ProfFlags.doHeapProfile == HEAP_BY_LDV) {
908                     if (prim)
909                         census->prim += real_size;
910                     else if ((LDVW(p) & LDV_STATE_MASK) == LDV_STATE_CREATE)
911                         census->not_used += real_size;
912                     else
913                         census->used += real_size;
914                 } else
915 #endif
916                 {
917                     identity = closureIdentity((StgClosure *)p);
918
919                     if (identity != NULL) {
920                         ctr = lookupHashTable( census->hash, (StgWord)identity );
921                         if (ctr != NULL) {
922 #ifdef PROFILING
923                             if (RtsFlags.ProfFlags.bioSelector != NULL) {
924                                 if (prim)
925                                     ctr->c.ldv.prim += real_size;
926                                 else if ((LDVW(p) & LDV_STATE_MASK) == LDV_STATE_CREATE)
927                                     ctr->c.ldv.not_used += real_size;
928                                 else
929                                     ctr->c.ldv.used += real_size;
930                             } else
931 #endif
932                             {
933                                 ctr->c.resid += real_size;
934                             }
935                         } else {
936                             ctr = arenaAlloc( census->arena, sizeof(counter) );
937                             initLDVCtr(ctr);
938                             insertHashTable( census->hash, (StgWord)identity, ctr );
939                             ctr->identity = identity;
940                             ctr->next = census->ctrs;
941                             census->ctrs = ctr;
942
943 #ifdef PROFILING
944                             if (RtsFlags.ProfFlags.bioSelector != NULL) {
945                                 if (prim)
946                                     ctr->c.ldv.prim = real_size;
947                                 else if ((LDVW(p) & LDV_STATE_MASK) == LDV_STATE_CREATE)
948                                     ctr->c.ldv.not_used = real_size;
949                                 else
950                                     ctr->c.ldv.used = real_size;
951                             } else
952 #endif
953                             {
954                                 ctr->c.resid = real_size;
955                             }
956                         }
957                     }
958                 }
959             }
960
961             p += size;
962         }
963     }
964 }
965
966 void
967 heapCensus( void )
968 {
969   nat g, s;
970   Census *census;
971
972   census = &censuses[era];
973   census->time  = mut_user_time();
974     
975   // calculate retainer sets if necessary
976 #ifdef PROFILING
977   if (doingRetainerProfiling()) {
978       retainerProfile();
979   }
980 #endif
981
982 #ifdef PROFILING
983   stat_startHeapCensus();
984 #endif
985
986   // traverse the heap, collecting the census info
987   heapCensusChain( census, small_alloc_list );
988   if (RtsFlags.GcFlags.generations == 1) {
989       heapCensusChain( census, g0s0->to_blocks );
990   } else {
991       for (g = 0; g < RtsFlags.GcFlags.generations; g++) {
992           for (s = 0; s < generations[g].n_steps; s++) {
993               heapCensusChain( census, generations[g].steps[s].blocks );
994               // Are we interested in large objects?  might be
995               // confusing to include the stack in a heap profile.
996               // heapCensusChain( census, generations[g].steps[s].large_objects );
997           }
998       }
999   }
1000
1001   // dump out the census info
1002 #ifdef PROFILING
1003     // We can't generate any info for LDV profiling until
1004     // the end of the run...
1005     if (!doingLDVProfiling())
1006         dumpCensus( census );
1007 #else
1008     dumpCensus( census );
1009 #endif
1010
1011
1012   // free our storage, unless we're keeping all the census info for
1013   // future restriction by biography.
1014 #ifdef PROFILING
1015   if (RtsFlags.ProfFlags.bioSelector == NULL)
1016 #endif
1017   {
1018       freeHashTable( census->hash, NULL/* don't free the elements */ );
1019       arenaFree( census->arena );
1020       census->hash = NULL;
1021       census->arena = NULL;
1022   }
1023
1024   // we're into the next time period now
1025   nextEra();
1026
1027 #ifdef PROFILING
1028   stat_endHeapCensus();
1029 #endif
1030 }    
1031
1032 #endif /* PROFILING || DEBUG_HEAP_PROF */
1033