[project @ 2001-12-12 15:59:33 by simonmar]
[ghc-hetmet.git] / ghc / rts / ProfHeap.c
1 /* -----------------------------------------------------------------------------
2  * $Id: ProfHeap.c,v 1.35 2001/12/12 15:59:33 simonmar 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             written = snprintf(buf+next_offset, 
502                                (int)max_length-3-(int)next_offset,
503                                "%s.CAF", ccs->cc->module);
504         } else {
505             if (ccs->prevStack != NULL && ccs->prevStack != CCS_MAIN) {
506                 template = "%s/";
507             } else {
508                 template = "%s";
509             }
510             written = snprintf(buf+next_offset, 
511                                (int)max_length-3-(int)next_offset,
512                                template, ccs->cc->label);
513         }
514
515         if (next_offset+written >= max_length-4) {
516             sprintf(buf+max_length-4, "...");
517             break;
518         } else {
519             next_offset += written;
520         }
521     }
522     fprintf(fp, "%s", buf);
523 }
524 #endif // PROFILING
525
526 rtsBool
527 strMatchesSelector( char* str, char* sel )
528 {
529    char* p;
530    // fprintf(stderr, "str_matches_selector %s %s\n", str, sel);
531    while (1) {
532        // Compare str against wherever we've got to in sel.
533        p = str;
534        while (*p != '\0' && *sel != ',' && *sel != '\0' && *p == *sel) {
535            p++; sel++;
536        }
537        // Match if all of str used and have reached the end of a sel fragment.
538        if (*p == '\0' && (*sel == ',' || *sel == '\0'))
539            return rtsTrue;
540        
541        // No match.  Advance sel to the start of the next elem.
542        while (*sel != ',' && *sel != '\0') sel++;
543        if (*sel == ',') sel++;
544        
545        /* Run out of sel ?? */
546        if (*sel == '\0') return rtsFalse;
547    }
548 }
549
550 /* -----------------------------------------------------------------------------
551  * Figure out whether a closure should be counted in this census, by
552  * testing against all the specified constraints.
553  * -------------------------------------------------------------------------- */
554 rtsBool
555 closureSatisfiesConstraints( StgClosure* p )
556 {
557 #ifdef DEBUG_HEAP_PROF
558     return rtsTrue;
559 #else
560    rtsBool b;
561
562    // The CCS has a selected field to indicate whether this closure is
563    // deselected by not being mentioned in the module, CC, or CCS
564    // selectors.
565    if (!p->header.prof.ccs->selected) {
566        return rtsFalse;
567    }
568
569    if (RtsFlags.ProfFlags.descrSelector) {
570        b = strMatchesSelector( (get_itbl((StgClosure *)p))->prof.closure_desc,
571                                  RtsFlags.ProfFlags.descrSelector );
572        if (!b) return rtsFalse;
573    }
574    if (RtsFlags.ProfFlags.typeSelector) {
575        b = strMatchesSelector( (get_itbl((StgClosure *)p))->prof.closure_type,
576                                 RtsFlags.ProfFlags.typeSelector );
577        if (!b) return rtsFalse;
578    }
579    if (RtsFlags.ProfFlags.retainerSelector) {
580        RetainerSet *rs;
581        nat i;
582        rs = retainerSetOf((StgClosure *)p);
583        if (rs != NULL) {
584            for (i = 0; i < rs->num; i++) {
585                b = strMatchesSelector( rs->element[i]->cc->label,
586                                          RtsFlags.ProfFlags.retainerSelector );
587                if (b) return rtsTrue;
588            }
589        }
590        return rtsFalse;
591    }
592    return rtsTrue;
593 #endif /* PROFILING */
594 }
595
596 /* -----------------------------------------------------------------------------
597  * Aggregate the heap census info for biographical profiling
598  * -------------------------------------------------------------------------- */
599 #ifdef PROFILING
600 static void
601 aggregateCensusInfo( void )
602 {
603     HashTable *acc;
604     nat t;
605     counter *c, *d, *ctrs;
606     Arena *arena;
607
608     if (!doingLDVProfiling()) return;
609
610     // Aggregate the LDV counters when displaying by biography.
611     if (RtsFlags.ProfFlags.doHeapProfile == HEAP_BY_LDV) {
612         int void_total, drag_total;
613
614         // Now we compute void_total and drag_total for each census
615         void_total = 0;
616         drag_total = 0;
617         for (t = 1; t < era; t++) { // note: start at 1, not 0
618             void_total += censuses[t].void_total;
619             drag_total += censuses[t].drag_total;
620             censuses[t].void_total = void_total;
621             censuses[t].drag_total = drag_total;
622             ASSERT( censuses[t].void_total <= censuses[t].not_used );
623             ASSERT( censuses[t].drag_total <= censuses[t].used );
624         }
625         
626         return;
627     }
628
629     // otherwise... we're doing a heap profile that is restricted to
630     // some combination of lag, drag, void or use.  We've kept all the
631     // census info for all censuses so far, but we still need to
632     // aggregate the counters forwards.
633
634     arena = newArena();
635     acc = allocHashTable();
636     ctrs = NULL;
637
638     for (t = 1; t < era; t++) {
639
640         // first look through all the counters we're aggregating
641         for (c = ctrs; c != NULL; c = c->next) {
642             // if one of the totals is non-zero, then this closure
643             // type must be present in the heap at this census time...
644             d = lookupHashTable(censuses[t].hash, (StgWord)c->identity);
645
646             if (d == NULL) {
647                 // if this closure identity isn't present in the
648                 // census for this time period, then our running
649                 // totals *must* be zero.
650                 ASSERT(c->c.ldv.void_total == 0 && c->c.ldv.drag_total == 0);
651
652                 // fprintCCS(stderr,c->identity);
653                 // fprintf(stderr," census=%d void_total=%d drag_total=%d\n",
654                 //         t, c->c.ldv.void_total, c->c.ldv.drag_total);
655             } else {
656                 d->c.ldv.void_total += c->c.ldv.void_total;
657                 d->c.ldv.drag_total += c->c.ldv.drag_total;
658                 c->c.ldv.void_total =  d->c.ldv.void_total;
659                 c->c.ldv.drag_total =  d->c.ldv.drag_total;
660
661                 ASSERT( c->c.ldv.void_total >= 0 );
662                 ASSERT( c->c.ldv.drag_total >= 0 );
663             }
664         }
665
666         // now look through the counters in this census to find new ones
667         for (c = censuses[t].ctrs; c != NULL; c = c->next) {
668             d = lookupHashTable(acc, (StgWord)c->identity);
669             if (d == NULL) {
670                 d = arenaAlloc( arena, sizeof(counter) );
671                 initLDVCtr(d);
672                 insertHashTable( acc, (StgWord)c->identity, d );
673                 d->identity = c->identity;
674                 d->next = ctrs;
675                 ctrs = d;
676                 d->c.ldv.void_total = c->c.ldv.void_total;
677                 d->c.ldv.drag_total = c->c.ldv.drag_total;
678             }
679             ASSERT( c->c.ldv.void_total >= 0 );
680             ASSERT( c->c.ldv.drag_total >= 0 );
681         }
682     }
683
684     freeHashTable(acc, NULL);
685     arenaFree(arena);
686 }
687 #endif
688
689 /* -----------------------------------------------------------------------------
690  * Print out the results of a heap census.
691  * -------------------------------------------------------------------------- */
692 static void
693 dumpCensus( Census *census )
694 {
695     counter *ctr;
696     int count;
697
698     fprintf(hp_file, "BEGIN_SAMPLE %0.2f\n", census->time);
699
700 #ifdef PROFILING
701     if (RtsFlags.ProfFlags.doHeapProfile == HEAP_BY_LDV) {
702         fprintf(hp_file, "VOID\t%u\n", census->void_total * sizeof(W_));
703         fprintf(hp_file, "LAG\t%u\n", 
704                 (census->not_used - census->void_total) * sizeof(W_));
705         fprintf(hp_file, "USE\t%u\n", 
706                 (census->used - census->drag_total) * sizeof(W_));
707         fprintf(hp_file, "INHERENT_USE\t%u\n", 
708                 census->prim * sizeof(W_));
709         fprintf(hp_file, "DRAG\t%u\n", census->drag_total *
710                 sizeof(W_));
711         fprintf(hp_file, "END_SAMPLE %0.2f\n", census->time);
712         return;
713     }
714 #endif
715
716     for (ctr = census->ctrs; ctr != NULL; ctr = ctr->next) {
717
718 #ifdef PROFILING
719         if (RtsFlags.ProfFlags.bioSelector != NULL) {
720             count = 0;
721             if (strMatchesSelector("lag", RtsFlags.ProfFlags.bioSelector))
722                 count += ctr->c.ldv.not_used - ctr->c.ldv.void_total;
723             if (strMatchesSelector("drag", RtsFlags.ProfFlags.bioSelector))
724                 count += ctr->c.ldv.drag_total;
725             if (strMatchesSelector("void", RtsFlags.ProfFlags.bioSelector))
726                 count += ctr->c.ldv.void_total;
727             if (strMatchesSelector("use", RtsFlags.ProfFlags.bioSelector))
728                 count += ctr->c.ldv.used - ctr->c.ldv.drag_total;
729         } else
730 #endif
731         {
732             count = ctr->c.resid;
733         }
734
735         ASSERT( count >= 0 );
736
737         if (count == 0) continue;
738
739 #ifdef DEBUG_HEAP_PROF
740         switch (RtsFlags.ProfFlags.doHeapProfile) {
741         case HEAP_BY_INFOPTR:
742             fprintf(hp_file, "%s", lookupGHCName(ctr->identity));
743             break;
744         case HEAP_BY_CLOSURE_TYPE:
745             fprintf(hp_file, "%s", (char *)ctr->identity);
746             break;
747         }
748 #endif
749         
750 #ifdef PROFILING
751         switch (RtsFlags.ProfFlags.doHeapProfile) {
752         case HEAP_BY_CCS:
753             fprint_ccs(hp_file, (CostCentreStack *)ctr->identity, 25);
754             break;
755         case HEAP_BY_MOD:
756         case HEAP_BY_DESCR:
757         case HEAP_BY_TYPE:
758             fprintf(hp_file, "%s", (char *)ctr->identity);
759             break;
760         case HEAP_BY_RETAINER:
761         {
762             RetainerSet *rs = (RetainerSet *)ctr->identity;
763
764             // it might be the distinguished retainer set rs_MANY:
765             if (rs == &rs_MANY) {
766                 fprintf(hp_file, "MANY");
767                 break;
768             }
769
770             // Mark this retainer set by negating its id, because it
771             // has appeared in at least one census.  We print the
772             // values of all such retainer sets into the log file at
773             // the end.  A retainer set may exist but not feature in
774             // any censuses if it arose as the intermediate retainer
775             // set for some closure during retainer set calculation.
776             if (rs->id > 0)
777                 rs->id = -(rs->id);
778
779             // report in the unit of bytes: * sizeof(StgWord)
780             printRetainerSetShort(hp_file, rs);
781             break;
782         }
783         default:
784             barf("dumpCensus; doHeapProfile");
785         }
786 #endif
787
788         fprintf(hp_file, "\t%d\n", count * sizeof(W_));
789     }
790
791     fprintf(hp_file, "END_SAMPLE %0.2f\n", census->time);
792 }
793
794 /* -----------------------------------------------------------------------------
795  * Code to perform a heap census.
796  * -------------------------------------------------------------------------- */
797 static void
798 heapCensusChain( Census *census, bdescr *bd )
799 {
800     StgPtr p;
801     StgInfoTable *info;
802     void *identity;
803     nat size;
804     counter *ctr;
805     nat real_size;
806     rtsBool prim;
807
808     for (; bd != NULL; bd = bd->link) {
809         p = bd->start;
810         while (p < bd->free) {
811             info = get_itbl((StgClosure *)p);
812             prim = rtsFalse;
813             
814             switch (info->type) {
815
816             case CONSTR:
817             case FUN:
818             case THUNK:
819             case IND_PERM:
820             case IND_OLDGEN_PERM:
821             case CAF_BLACKHOLE:
822             case SE_CAF_BLACKHOLE:
823             case SE_BLACKHOLE:
824             case BLACKHOLE:
825             case BLACKHOLE_BQ:
826             case CONSTR_INTLIKE:
827             case CONSTR_CHARLIKE:
828             case FUN_1_0:
829             case FUN_0_1:
830             case FUN_1_1:
831             case FUN_0_2:
832             case FUN_2_0:
833             case THUNK_1_1:
834             case THUNK_0_2:
835             case THUNK_2_0:
836             case CONSTR_1_0:
837             case CONSTR_0_1:
838             case CONSTR_1_1:
839             case CONSTR_0_2:
840             case CONSTR_2_0:
841                 size = sizeW_fromITBL(info);
842                 break;
843                 
844             case BCO:
845             case MVAR:
846             case WEAK:
847             case FOREIGN:
848             case STABLE_NAME:
849             case MUT_VAR:
850             case MUT_CONS:
851                 prim = rtsTrue;
852                 size = sizeW_fromITBL(info);
853                 break;
854
855             case THUNK_1_0:             /* ToDo - shouldn't be here */
856             case THUNK_0_1:             /* "  ditto  " */
857             case THUNK_SELECTOR:
858                 size = sizeofW(StgHeader) + MIN_UPD_SIZE;
859                 break;
860
861             case PAP:
862             case AP_UPD:
863                 size = pap_sizeW((StgPAP *)p);
864                 break;
865                 
866             case ARR_WORDS:
867                 prim = rtsTrue;
868                 size = arr_words_sizeW(stgCast(StgArrWords*,p));
869                 break;
870                 
871             case MUT_ARR_PTRS:
872             case MUT_ARR_PTRS_FROZEN:
873                 prim = rtsTrue;
874                 size = mut_arr_ptrs_sizeW((StgMutArrPtrs *)p);
875                 break;
876                 
877             case TSO:
878                 prim = rtsTrue;
879                 size = tso_sizeW((StgTSO *)p);
880                 break;
881                 
882             default:
883                 barf("heapCensus");
884             }
885             
886             identity = NULL;
887
888 #ifdef DEBUG_HEAP_PROF
889             real_size = size;
890 #else
891             // subtract the profiling overhead
892             real_size = size - sizeofW(StgProfHeader);
893 #endif
894
895             if (closureSatisfiesConstraints((StgClosure*)p)) {
896 #ifdef PROFILING
897                 if (RtsFlags.ProfFlags.doHeapProfile == HEAP_BY_LDV) {
898                     if (prim)
899                         census->prim += real_size;
900                     else if ((LDVW(p) & LDV_STATE_MASK) == LDV_STATE_CREATE)
901                         census->not_used += real_size;
902                     else
903                         census->used += real_size;
904                 } else
905 #endif
906                 {
907                     identity = closureIdentity((StgClosure *)p);
908
909                     if (identity != NULL) {
910                         ctr = lookupHashTable( census->hash, (StgWord)identity );
911                         if (ctr != NULL) {
912 #ifdef PROFILING
913                             if (RtsFlags.ProfFlags.bioSelector != NULL) {
914                                 if (prim)
915                                     ctr->c.ldv.prim += real_size;
916                                 else if ((LDVW(p) & LDV_STATE_MASK) == LDV_STATE_CREATE)
917                                     ctr->c.ldv.not_used += real_size;
918                                 else
919                                     ctr->c.ldv.used += real_size;
920                             } else
921 #endif
922                             {
923                                 ctr->c.resid += real_size;
924                             }
925                         } else {
926                             ctr = arenaAlloc( census->arena, sizeof(counter) );
927                             initLDVCtr(ctr);
928                             insertHashTable( census->hash, (StgWord)identity, ctr );
929                             ctr->identity = identity;
930                             ctr->next = census->ctrs;
931                             census->ctrs = ctr;
932
933 #ifdef PROFILING
934                             if (RtsFlags.ProfFlags.bioSelector != NULL) {
935                                 if (prim)
936                                     ctr->c.ldv.prim = real_size;
937                                 else if ((LDVW(p) & LDV_STATE_MASK) == LDV_STATE_CREATE)
938                                     ctr->c.ldv.not_used = real_size;
939                                 else
940                                     ctr->c.ldv.used = real_size;
941                             } else
942 #endif
943                             {
944                                 ctr->c.resid = real_size;
945                             }
946                         }
947                     }
948                 }
949             }
950
951             p += size;
952         }
953     }
954 }
955
956 void
957 heapCensus( void )
958 {
959   nat g, s;
960   Census *census;
961
962   census = &censuses[era];
963   census->time  = mut_user_time();
964     
965   // calculate retainer sets if necessary
966 #ifdef PROFILING
967   if (doingRetainerProfiling()) {
968       retainerProfile();
969   }
970 #endif
971
972 #ifdef PROFILING
973   stat_startHeapCensus();
974 #endif
975
976   // traverse the heap, collecting the census info
977   heapCensusChain( census, small_alloc_list );
978   if (RtsFlags.GcFlags.generations == 1) {
979       heapCensusChain( census, g0s0->to_blocks );
980   } else {
981       for (g = 0; g < RtsFlags.GcFlags.generations; g++) {
982           for (s = 0; s < generations[g].n_steps; s++) {
983               heapCensusChain( census, generations[g].steps[s].blocks );
984               // Are we interested in large objects?  might be
985               // confusing to include the stack in a heap profile.
986               // heapCensusChain( census, generations[g].steps[s].large_objects );
987           }
988       }
989   }
990
991   // dump out the census info
992 #ifdef PROFILING
993     // We can't generate any info for LDV profiling until
994     // the end of the run...
995     if (!doingLDVProfiling())
996         dumpCensus( census );
997 #else
998     dumpCensus( census );
999 #endif
1000
1001
1002   // free our storage, unless we're keeping all the census info for
1003   // future restriction by biography.
1004 #ifdef PROFILING
1005   if (RtsFlags.ProfFlags.bioSelector == NULL)
1006 #endif
1007   {
1008       freeHashTable( census->hash, NULL/* don't free the elements */ );
1009       arenaFree( census->arena );
1010       census->hash = NULL;
1011       census->arena = NULL;
1012   }
1013
1014   // we're into the next time period now
1015   nextEra();
1016
1017 #ifdef PROFILING
1018   stat_endHeapCensus();
1019 #endif
1020 }    
1021
1022 #endif /* PROFILING || DEBUG_HEAP_PROF */
1023