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