[project @ 2001-11-28 17:45:13 by simonmar]
[ghc-hetmet.git] / ghc / rts / ProfHeap.c
1 /* -----------------------------------------------------------------------------
2  * $Id: ProfHeap.c,v 1.31 2001/11/28 17:45:13 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 (doingLDVProfiling()) {
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         return;
621     }
622
623     // otherwise... we're doing a heap profile that is restricted to
624     // some combination of lag, drag, void or use.  We've kept all the
625     // census info for all censuses so far, but we still need to
626     // aggregate the counters forwards.
627
628     arena = newArena();
629     acc = allocHashTable();
630     ctrs = NULL;
631
632     for (t = 1; t < era; t++) {
633
634         // first look through all the counters we're aggregating
635         for (c = ctrs; c != NULL; c = c->next) {
636             // if one of the totals is non-zero, then this closure
637             // type must be present in the heap at this census time...
638             d = lookupHashTable(censuses[t].hash, (StgWord)c->identity);
639
640             if (d == NULL) {
641                 // if this closure identity isn't present in the
642                 // census for this time period, then our running
643                 // totals *must* be zero.
644                 ASSERT(c->c.ldv.void_total == 0 && c->c.ldv.drag_total == 0);
645
646                 // fprintCCS(stderr,c->identity);
647                 // fprintf(stderr," census=%d void_total=%d drag_total=%d\n",
648                 //         t, c->c.ldv.void_total, c->c.ldv.drag_total);
649             } else {
650                 d->c.ldv.void_total += c->c.ldv.void_total;
651                 d->c.ldv.drag_total += c->c.ldv.drag_total;
652                 c->c.ldv.void_total =  d->c.ldv.void_total;
653                 c->c.ldv.drag_total =  d->c.ldv.drag_total;
654
655                 ASSERT( c->c.ldv.void_total >= 0 );
656                 ASSERT( c->c.ldv.drag_total >= 0 );
657             }
658         }
659
660         // now look through the counters in this census to find new ones
661         for (c = censuses[t].ctrs; c != NULL; c = c->next) {
662             d = lookupHashTable(acc, (StgWord)c->identity);
663             if (d == NULL) {
664                 d = arenaAlloc( arena, sizeof(counter) );
665                 initLDVCtr(d);
666                 insertHashTable( acc, (StgWord)c->identity, d );
667                 d->identity = c->identity;
668                 d->next = ctrs;
669                 ctrs = d;
670                 d->c.ldv.void_total = c->c.ldv.void_total;
671                 d->c.ldv.drag_total = c->c.ldv.drag_total;
672             }
673             ASSERT( c->c.ldv.void_total >= 0 );
674             ASSERT( c->c.ldv.drag_total >= 0 );
675         }
676     }
677
678     freeHashTable(acc, NULL);
679     arenaFree(arena);
680 }
681 #endif
682
683 /* -----------------------------------------------------------------------------
684  * Print out the results of a heap census.
685  * -------------------------------------------------------------------------- */
686 static void
687 dumpCensus( Census *census )
688 {
689     counter *ctr;
690     int count;
691
692     fprintf(hp_file, "BEGIN_SAMPLE %0.2f\n", census->time);
693
694 #ifdef PROFILING
695     if (RtsFlags.ProfFlags.doHeapProfile == HEAP_BY_LDV) {
696         fprintf(hp_file, "VOID\t%u\n", census->void_total * sizeof(W_));
697         fprintf(hp_file, "LAG\t%u\n", 
698                 (census->not_used - census->void_total) * sizeof(W_));
699         fprintf(hp_file, "USE\t%u\n", 
700                 (census->used - census->drag_total) * sizeof(W_));
701         fprintf(hp_file, "INHERENT_USE\t%u\n", 
702                 census->prim * sizeof(W_));
703         fprintf(hp_file, "DRAG\t%u\n", census->drag_total *
704                 sizeof(W_));
705         fprintf(hp_file, "END_SAMPLE %0.2f\n", census->time);
706         return;
707     }
708 #endif
709
710     for (ctr = census->ctrs; ctr != NULL; ctr = ctr->next) {
711
712 #ifdef PROFILING
713         if (RtsFlags.ProfFlags.bioSelector != NULL) {
714             count = 0;
715             if (str_matches_selector("lag", RtsFlags.ProfFlags.bioSelector))
716                 count += ctr->c.ldv.not_used - ctr->c.ldv.void_total;
717             if (str_matches_selector("drag", RtsFlags.ProfFlags.bioSelector))
718                 count += ctr->c.ldv.drag_total;
719             if (str_matches_selector("void", RtsFlags.ProfFlags.bioSelector))
720                 count += ctr->c.ldv.void_total;
721             if (str_matches_selector("use", RtsFlags.ProfFlags.bioSelector))
722                 count += ctr->c.ldv.used - ctr->c.ldv.drag_total;
723         } else
724 #endif
725         {
726             count = ctr->c.resid;
727         }
728
729         ASSERT( count >= 0 );
730
731         if (count == 0) continue;
732
733 #ifdef DEBUG_HEAP_PROF
734         switch (RtsFlags.ProfFlags.doHeapProfile) {
735         case HEAP_BY_INFOPTR:
736             fprintf(hp_file, "%s", lookupGHCName(ctr->identity));
737             break;
738         case HEAP_BY_CLOSURE_TYPE:
739             fprintf(hp_file, "%s", (char *)ctr->identity);
740             break;
741         }
742 #endif
743         
744 #ifdef PROFILING
745         switch (RtsFlags.ProfFlags.doHeapProfile) {
746         case HEAP_BY_CCS:
747             fprint_ccs(hp_file, (CostCentreStack *)ctr->identity, 30);
748             break;
749         case HEAP_BY_MOD:
750         case HEAP_BY_DESCR:
751         case HEAP_BY_TYPE:
752             fprintf(hp_file, "%s", (char *)ctr->identity);
753             break;
754         case HEAP_BY_RETAINER:
755         {
756             RetainerSet *rs = (RetainerSet *)ctr->identity;
757
758             // it might be the distinguished retainer set rs_MANY:
759             if (rs == &rs_MANY) {
760                 fprintf(hp_file, "MANY");
761                 break;
762             }
763
764             // Mark this retainer set by negating its id, because it
765             // has appeared in at least one census.  We print the
766             // values of all such retainer sets into the log file at
767             // the end.  A retainer set may exist but not feature in
768             // any censuses if it arose as the intermediate retainer
769             // set for some closure during retainer set calculation.
770             if (rs->id > 0)
771                 rs->id = -(rs->id);
772
773             // report in the unit of bytes: * sizeof(StgWord)
774             printRetainerSetShort(hp_file, rs);
775             break;
776         }
777         default:
778             barf("dumpCensus; doHeapProfile");
779         }
780 #endif
781
782         fprintf(hp_file, "\t%d\n", count * sizeof(W_));
783     }
784
785     fprintf(hp_file, "END_SAMPLE %0.2f\n", census->time);
786 }
787
788 /* -----------------------------------------------------------------------------
789  * Code to perform a heap census.
790  * -------------------------------------------------------------------------- */
791 static void
792 heapCensusChain( Census *census, bdescr *bd )
793 {
794     StgPtr p;
795     StgInfoTable *info;
796     void *identity;
797     nat size;
798     counter *ctr;
799     nat real_size;
800     rtsBool prim;
801
802     for (; bd != NULL; bd = bd->link) {
803         p = bd->start;
804         while (p < bd->free) {
805             info = get_itbl((StgClosure *)p);
806             prim = rtsFalse;
807             
808             switch (info->type) {
809
810             case CONSTR:
811             case FUN:
812             case THUNK:
813             case IND_PERM:
814             case IND_OLDGEN_PERM:
815             case CAF_BLACKHOLE:
816             case SE_CAF_BLACKHOLE:
817             case SE_BLACKHOLE:
818             case BLACKHOLE:
819             case BLACKHOLE_BQ:
820             case CONSTR_INTLIKE:
821             case CONSTR_CHARLIKE:
822             case FUN_1_0:
823             case FUN_0_1:
824             case FUN_1_1:
825             case FUN_0_2:
826             case FUN_2_0:
827             case THUNK_1_1:
828             case THUNK_0_2:
829             case THUNK_2_0:
830             case CONSTR_1_0:
831             case CONSTR_0_1:
832             case CONSTR_1_1:
833             case CONSTR_0_2:
834             case CONSTR_2_0:
835                 size = sizeW_fromITBL(info);
836                 break;
837                 
838             case BCO:
839             case MVAR:
840             case WEAK:
841             case FOREIGN:
842             case STABLE_NAME:
843             case MUT_VAR:
844             case MUT_CONS:
845                 prim = rtsTrue;
846                 size = sizeW_fromITBL(info);
847                 break;
848
849             case THUNK_1_0:             /* ToDo - shouldn't be here */
850             case THUNK_0_1:             /* "  ditto  " */
851             case THUNK_SELECTOR:
852                 size = sizeofW(StgHeader) + MIN_UPD_SIZE;
853                 break;
854
855             case PAP:
856             case AP_UPD:
857                 size = pap_sizeW((StgPAP *)p);
858                 break;
859                 
860             case ARR_WORDS:
861                 prim = rtsTrue;
862                 size = arr_words_sizeW(stgCast(StgArrWords*,p));
863                 break;
864                 
865             case MUT_ARR_PTRS:
866             case MUT_ARR_PTRS_FROZEN:
867                 prim = rtsTrue;
868                 size = mut_arr_ptrs_sizeW((StgMutArrPtrs *)p);
869                 break;
870                 
871             case TSO:
872                 prim = rtsTrue;
873                 size = tso_sizeW((StgTSO *)p);
874                 break;
875                 
876             default:
877                 barf("heapCensus");
878             }
879             
880             identity = NULL;
881
882 #ifdef DEBUG_HEAP_PROF
883             real_size = size;
884 #else
885             // subtract the profiling overhead
886             real_size = size - sizeofW(StgProfHeader);
887 #endif
888
889             if (closureSatisfiesConstraints((StgClosure*)p)) {
890 #ifdef PROFILING
891                 if (RtsFlags.ProfFlags.doHeapProfile == HEAP_BY_LDV) {
892                     if (prim)
893                         census->prim += real_size;
894                     else if ((LDVW(p) & LDV_STATE_MASK) == LDV_STATE_CREATE)
895                         census->not_used += real_size;
896                     else
897                         census->used += real_size;
898                 } else
899 #endif
900                 {
901                     identity = closureIdentity((StgClosure *)p);
902
903                     if (identity != NULL) {
904                         ctr = lookupHashTable( census->hash, (StgWord)identity );
905                         if (ctr != NULL) {
906 #ifdef PROFILING
907                             if (RtsFlags.ProfFlags.bioSelector != NULL) {
908                                 if (prim)
909                                     ctr->c.ldv.prim += real_size;
910                                 else if ((LDVW(p) & LDV_STATE_MASK) == LDV_STATE_CREATE)
911                                     ctr->c.ldv.not_used += real_size;
912                                 else
913                                     ctr->c.ldv.used += real_size;
914                             } else
915 #endif
916                             {
917                                 ctr->c.resid += real_size;
918                             }
919                         } else {
920                             ctr = arenaAlloc( census->arena, sizeof(counter) );
921                             initLDVCtr(ctr);
922                             insertHashTable( census->hash, (StgWord)identity, ctr );
923                             ctr->identity = identity;
924                             ctr->next = census->ctrs;
925                             census->ctrs = ctr;
926
927 #ifdef PROFILING
928                             if (RtsFlags.ProfFlags.bioSelector != NULL) {
929                                 if (prim)
930                                     ctr->c.ldv.prim = real_size;
931                                 else if ((LDVW(p) & LDV_STATE_MASK) == LDV_STATE_CREATE)
932                                     ctr->c.ldv.not_used = real_size;
933                                 else
934                                     ctr->c.ldv.used = real_size;
935                             } else
936 #endif
937                             {
938                                 ctr->c.resid = real_size;
939                             }
940                         }
941                     }
942                 }
943             }
944
945             p += size;
946         }
947     }
948 }
949
950 void
951 heapCensus( void )
952 {
953   nat g, s;
954   Census *census;
955
956   census = &censuses[era];
957   census->time  = mut_user_time();
958     
959   // calculate retainer sets if necessary
960 #ifdef PROFILING
961   if (doingRetainerProfiling()) {
962       retainerProfile();
963   }
964 #endif
965
966 #ifdef PROFILING
967   stat_startHeapCensus();
968 #endif
969
970   // traverse the heap, collecting the census info
971   heapCensusChain( census, small_alloc_list );
972   if (RtsFlags.GcFlags.generations == 1) {
973       heapCensusChain( census, g0s0->to_blocks );
974   } else {
975       for (g = 0; g < RtsFlags.GcFlags.generations; g++) {
976           for (s = 0; s < generations[g].n_steps; s++) {
977               heapCensusChain( census, generations[g].steps[s].blocks );
978               // Are we interested in large objects?  might be
979               // confusing to include the stack in a heap profile.
980               // heapCensusChain( census, generations[g].steps[s].large_objects );
981           }
982       }
983   }
984
985   // dump out the census info
986 #ifdef PROFILING
987     // We can't generate any info for LDV profiling until
988     // the end of the run...
989     if (!doingLDVProfiling())
990         dumpCensus( census );
991 #else
992     dumpCensus( census );
993 #endif
994
995
996   // free our storage, unless we're keeping all the census info for
997   // future restriction by biography.
998 #ifdef PROFILING
999   if (RtsFlags.ProfFlags.bioSelector == NULL)
1000 #endif
1001   {
1002       freeHashTable( census->hash, NULL/* don't free the elements */ );
1003       arenaFree( census->arena );
1004       census->hash = NULL;
1005       census->arena = NULL;
1006   }
1007
1008   // we're into the next time period now
1009   nextEra();
1010
1011 #ifdef PROFILING
1012   stat_endHeapCensus();
1013 #endif
1014 }    
1015
1016 #endif /* PROFILING || DEBUG_HEAP_PROF */
1017