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