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