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