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