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