[project @ 2001-11-22 14:25:11 by simonmar]
[ghc-hetmet.git] / ghc / rts / ProfHeap.c
1 /* -----------------------------------------------------------------------------
2  * $Id: ProfHeap.c,v 1.26 2001/11/22 14:25:12 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
31 #ifdef DEBUG_HEAP_PROF
32 #include "Printer.h"
33 static void initSymbolHash(void);
34 static void clear_table_data(void);
35 static void fprint_data(FILE *fp);
36 #endif
37
38 /* -----------------------------------------------------------------------------
39  * Hash tables.
40  *
41  * For profiling by module, constructor or closure type we need to be
42  * able to get from a string describing the category to a structure
43  * containing the counters for that category.  The strings aren't
44  * unique (although gcc will do a fairly good job of commoning them up
45  * where possible), so we have a many->one mapping.
46  *
47  * We represent the many->one mapping with a hash table.  In order to
48  * find the unique counter associated with a string the first time we
49  * encounter a particular string, we need another hash table, mapping
50  * hashed strings to buckets of counters.  The string is hashed, then
51  * the bucket is searched for an existing counter for the same
52  * string. 
53  *
54  * -------------------------------------------------------------------------- */
55
56 #ifdef PROFILING
57 typedef struct _ctr {
58     const char *str;
59     unsigned long mem_resid;
60     struct _ctr *next;
61     struct _ctr *next_bucket;
62 } prof_ctr;
63
64 /* Linked list of all existing ctr structs */
65 prof_ctr *all_ctrs;
66
67 /* Hash table mapping (char *) -> (struct _ctr) */
68 HashTable *str_to_ctr;
69
70 /* Hash table mapping hash_t (hashed string) -> (struct _ctr) */
71 HashTable *hashstr_to_ctrs;
72
73 static void
74 initHashTables( void )
75 {
76     str_to_ctr      = allocHashTable();
77     hashstr_to_ctrs = allocHashTable();
78     all_ctrs = NULL;
79 }
80
81 static prof_ctr *
82 strToCtr(const char *str)
83 {
84     prof_ctr *ctr;
85
86     ctr = lookupHashTable( str_to_ctr, (W_)str );
87
88     if (ctr != NULL) { return ctr; }
89
90     else {
91         hash_t str_hash = hash_str((char *)str);
92         prof_ctr *prev;
93
94         ctr = lookupHashTable( hashstr_to_ctrs, (W_)str_hash );
95         prev = NULL;
96
97         for (; ctr != NULL; prev = ctr, ctr = ctr->next_bucket ) {
98             if (!strcmp(ctr->str, str)) {
99                 insertHashTable( str_to_ctr, (W_)str, ctr );
100 #ifdef DEBUG_CTR
101                 fprintf(stderr,"strToCtr: existing ctr for `%s'\n",str);
102 #endif
103                 return ctr;
104             }
105         }
106
107         ctr = stgMallocBytes(sizeof(prof_ctr), "strToCtr");
108         ctr->mem_resid = 0;
109         ctr->str = str;
110         ctr->next_bucket = NULL;
111         ctr->next = all_ctrs;
112         all_ctrs = ctr;
113
114 #ifdef DEBUG_CTR
115         fprintf(stderr,"strToCtr: new ctr for `%s'\n",str);
116 #endif
117
118         if (prev != NULL) {
119             prev->next_bucket = ctr;
120         } else {
121             insertHashTable( hashstr_to_ctrs, str_hash, ctr );
122         }
123         insertHashTable( str_to_ctr, (W_)str, ctr);
124         return ctr;
125     }
126 }
127
128 static void
129 clearCtrResid( void )
130 {
131     prof_ctr *ctr;
132     
133     for (ctr = all_ctrs; ctr != NULL; ctr = ctr->next) {
134         ctr->mem_resid = 0;
135     }
136 }
137
138 static void
139 reportCtrResid(FILE *fp)
140 {
141     prof_ctr *ctr;
142     
143     for (ctr = all_ctrs; ctr != NULL; ctr = ctr->next) {
144         if (ctr->mem_resid != 0) {
145             fprintf(fp,"   %s %ld\n", ctr->str, ctr->mem_resid * sizeof(W_));
146         }
147     }
148 }
149 #endif /* PROFILING */
150
151 /* -------------------------------------------------------------------------- */
152
153 #ifdef DEBUG_HEAP_PROF
154 FILE *hp_file;
155
156 void initProfiling1( void )
157 {
158 }
159
160 void initProfiling2( void )
161 {
162   initHeapProfiling();
163 }
164
165 void endProfiling( void )
166 {
167   endHeapProfiling();
168 }
169 #endif /* DEBUG_HEAP_PROF */
170
171 nat
172 initHeapProfiling(void)
173 {
174     if (! RtsFlags.ProfFlags.doHeapProfile) {
175         return 0;
176     }
177
178     fprintf(hp_file, "JOB \"%s", prog_argv[0]);
179
180 #ifdef PROFILING
181     {
182         int count;
183         for(count = 1; count < prog_argc; count++)
184             fprintf(hp_file, " %s", prog_argv[count]);
185         fprintf(hp_file, " +RTS ");
186         for(count = 0; count < rts_argc; count++)
187             fprintf(hp_file, "%s ", rts_argv[count]);
188         fprintf(hp_file, "\n");
189     }
190 #endif /* PROFILING */
191
192     fprintf(hp_file, "\"\n" );
193
194     fprintf(hp_file, "DATE \"%s\"\n", time_str());
195
196     fprintf(hp_file, "SAMPLE_UNIT \"seconds\"\n");
197     fprintf(hp_file, "VALUE_UNIT \"bytes\"\n");
198
199     fprintf(hp_file, "BEGIN_SAMPLE 0.00\n");
200     fprintf(hp_file, "END_SAMPLE 0.00\n");
201
202 #ifdef DEBUG_HEAP_PROF
203     DEBUG_LoadSymbols(prog_argv[0]);
204     initSymbolHash();
205 #endif
206
207 #ifdef PROFILING
208     initHashTables();
209 #endif
210
211     return 0;
212 }
213
214 void
215 endHeapProfiling(void)
216 {
217     StgDouble seconds;
218
219     if (! RtsFlags.ProfFlags.doHeapProfile) {
220         return;
221     }
222
223 #ifdef PROFILING
224     switch (RtsFlags.ProfFlags.doHeapProfile) {
225     case HEAP_BY_RETAINER:
226         endRetainerProfiling();
227         break;
228     case HEAP_BY_LDV:
229         endLdvProfiling();
230         break;
231     }
232 #endif
233
234     seconds = mut_user_time();
235     fprintf(hp_file, "BEGIN_SAMPLE %0.2f\n", seconds);
236     fprintf(hp_file, "END_SAMPLE %0.2f\n", seconds);
237     fclose(hp_file);
238 }
239
240 #ifdef DEBUG_HEAP_PROF
241 /* -----------------------------------------------------------------------------
242    Hash table for symbols.
243    -------------------------------------------------------------------------- */
244
245 typedef struct {
246     const char *name;
247     void *ptr;
248     nat data;
249 } symbol_info;
250
251 #define SYMBOL_HASH_SIZE 0x3fff
252
253 symbol_info symbol_hash[SYMBOL_HASH_SIZE];
254
255 static inline nat
256 hash(void *ptr)
257 {
258     return ((W_)ptr)>>4 & 0x3fff;
259 }
260
261 static void
262 initSymbolHash(void)
263 {
264     nat i;
265
266     for (i=0; i < SYMBOL_HASH_SIZE; i++) {
267         symbol_hash[i].ptr = NULL;
268     }
269 }
270
271 static nat
272 lookup_symbol(void *addr)
273 {
274     nat orig_bucket = hash(addr);
275     nat bucket;
276
277     bucket = orig_bucket;
278     while (bucket < SYMBOL_HASH_SIZE && symbol_hash[bucket].ptr != NULL) {
279         if (symbol_hash[bucket].ptr == addr) {
280             return bucket;
281         }
282         bucket++;
283     }
284     if (bucket == SYMBOL_HASH_SIZE) {
285         bucket = 0;
286         while (bucket < orig_bucket && symbol_hash[bucket].ptr != NULL) {
287             if (symbol_hash[bucket].ptr == addr) {
288                 return bucket;
289             }
290             bucket++;
291         }
292         if (bucket == orig_bucket) {
293             barf("out of symbol table space");
294         }
295     }
296     
297     symbol_hash[bucket].ptr  = addr;
298     lookupGHCName(addr,&symbol_hash[bucket].name);
299     symbol_hash[bucket].data = 0;
300     return bucket;
301 }
302
303 static void
304 clear_table_data(void)
305 {
306     nat i;
307
308     for (i = 0; i < SYMBOL_HASH_SIZE; i++) {
309         symbol_hash[i].data = 0;
310     }
311 }
312
313 static void
314 fprint_data(FILE *fp)
315 {
316     nat i;
317     for (i = 0; i < SYMBOL_HASH_SIZE; i++) {
318         if (symbol_hash[i].data > 0) {
319             fprintf(fp, "   %s %lu\n", symbol_hash[i].name, (unsigned long)symbol_hash[i].data);
320         }
321     }
322 }
323
324 static inline void
325 add_data(void *addr, nat data)
326 {
327     symbol_hash[lookup_symbol(addr)].data += data;
328 }
329
330 /* -----------------------------------------------------------------------------
331    Closure Type Profiling;
332
333    PROBABLY TOTALLY OUT OF DATE -- ToDo (SDM)
334    -------------------------------------------------------------------------- */
335
336 static nat closure_types[N_CLOSURE_TYPES];
337
338 static char *type_names[] = {
339       "INVALID_OBJECT"
340     , "CONSTR"
341     , "CONSTR_INTLIKE"
342     , "CONSTR_CHARLIKE"
343     , "CONSTR_STATIC"
344     , "CONSTR_NOCAF_STATIC"
345
346     , "FUN"
347     , "FUN_STATIC"
348
349     , "THUNK"
350     , "THUNK_STATIC"
351     , "THUNK_SELECTOR"
352
353     , "BCO"
354     , "AP_UPD"
355
356     , "PAP"
357
358     , "IND"
359     , "IND_OLDGEN"
360     , "IND_PERM"
361     , "IND_OLDGEN_PERM"
362     , "IND_STATIC"
363
364     , "RET_BCO"
365     , "RET_SMALL"
366     , "RET_VEC_SMALL"
367     , "RET_BIG"
368     , "RET_VEC_BIG"
369     , "RET_DYN"
370     , "UPDATE_FRAME"
371     , "CATCH_FRAME"
372     , "STOP_FRAME"
373     , "SEQ_FRAME"
374
375     , "BLACKHOLE"
376     , "BLACKHOLE_BQ"
377     , "MVAR"
378
379     , "ARR_WORDS"
380
381     , "MUT_ARR_PTRS"
382     , "MUT_ARR_PTRS_FROZEN"
383     , "MUT_VAR"
384
385     , "WEAK"
386     , "FOREIGN"
387   
388     , "TSO"
389
390     , "BLOCKED_FETCH"
391     , "FETCH_ME"
392
393     , "EVACUATED"
394 };
395
396 static void 
397 fprint_closure_types(FILE *fp)
398 {
399   nat i;
400
401   for (i = 0; i < N_CLOSURE_TYPES; i++) {
402     if (closure_types[i]) {
403       fprintf(fp, "   %s %lu\n", type_names[i], (unsigned long)closure_types[i]);
404     }
405   }
406 }
407
408 #endif /* DEBUG_HEAP_PROF */
409
410
411 #ifdef PROFILING
412 static void
413 clearCCSResid(CostCentreStack *ccs)
414 {
415   IndexTable *i;
416
417   ccs->mem_resid = 0;
418
419   for (i = ccs->indexTable; i != 0; i = i->next) {
420     if (!i->back_edge) {
421       clearCCSResid(i->ccs);
422     }
423   }
424 }
425
426 static void
427 fprint_ccs(FILE *fp, CostCentreStack *ccs, nat max_length)
428 {
429     char buf[max_length+1];
430     nat next_offset = 0;
431     nat written;
432     char *template;
433
434     // MAIN on its own gets printed as "MAIN", otherwise we ignore MAIN.
435     if (ccs == CCS_MAIN) {
436         fprintf(fp, "MAIN");
437         return;
438     }
439
440     // keep printing components of the stack until we run out of space
441     // in the buffer.  If we run out of space, end with "...".
442     for (; ccs != NULL && ccs != CCS_MAIN; ccs = ccs->prevStack) {
443
444         // CAF cost centres print as M.CAF, but we leave the module
445         // name out of all the others to save space.
446         if (!strcmp(ccs->cc->label,"CAF")) {
447             written = snprintf(buf+next_offset, 
448                                (int)max_length-3-(int)next_offset,
449                                "%s.CAF", ccs->cc->module);
450         } else {
451             if (ccs->prevStack != NULL && ccs->prevStack != CCS_MAIN) {
452                 template = "%s/";
453             } else {
454                 template = "%s";
455             }
456             written = snprintf(buf+next_offset, 
457                                (int)max_length-3-(int)next_offset,
458                                template, ccs->cc->label);
459         }
460
461         if (next_offset+written >= max_length-4) {
462             sprintf(buf+max_length-4, "...");
463             break;
464         } else {
465             next_offset += written;
466         }
467     }
468     fprintf(fp, "%s", buf);
469 }
470
471 static void
472 reportCCSResid(FILE *fp, CostCentreStack *ccs)
473 {
474   IndexTable *i;
475
476   if (ccs->mem_resid != 0) {
477     fprintf(fp,"   ");
478     // print as much of the CCS as possible in 20 chars, ending with "..."
479     fprint_ccs(fp,ccs,30);
480     fprintf(fp," %ld\n", ccs->mem_resid * sizeof(W_));
481   }
482
483   for (i = ccs->indexTable; i != 0; i = i->next) {
484     if (!i->back_edge) {
485       reportCCSResid(fp,i->ccs);
486     }
487   }
488 }
489
490 static rtsBool
491 str_matches_selector( char* str, char* sel )
492 {
493    char* p;
494    // fprintf(stderr, "str_matches_selector %s %s\n", str, sel);
495    while (1) {
496        // Compare str against wherever we've got to in sel.
497        p = str;
498        while (*p != '\0' && *sel != ',' && *sel != '\0' && *p == *sel) {
499            p++; sel++;
500        }
501        // Match if all of str used and have reached the end of a sel fragment.
502        if (*p == '\0' && (*sel == ',' || *sel == '\0'))
503            return rtsTrue;
504        
505        // No match.  Advance sel to the start of the next elem.
506        while (*sel != ',' && *sel != '\0') sel++;
507        if (*sel == ',') sel++;
508        
509        /* Run out of sel ?? */
510        if (*sel == '\0') return rtsFalse;
511    }
512 }
513
514 // Figure out whether a closure should be counted in this census, by
515 // testing against all the specified constraints.
516 rtsBool
517 closureSatisfiesConstraints( StgClosure* p )
518 {
519    rtsBool b;
520    if (RtsFlags.ProfFlags.modSelector) {
521        b = str_matches_selector( ((StgClosure *)p)->header.prof.ccs->cc->module,
522                                  RtsFlags.ProfFlags.modSelector );
523        if (!b) return rtsFalse;
524    }
525    if (RtsFlags.ProfFlags.descrSelector) {
526        b = str_matches_selector( (get_itbl((StgClosure *)p))->prof.closure_desc,
527                                  RtsFlags.ProfFlags.descrSelector );
528        if (!b) return rtsFalse;
529    }
530    if (RtsFlags.ProfFlags.typeSelector) {
531        b = str_matches_selector( (get_itbl((StgClosure *)p))->prof.closure_type,
532                                 RtsFlags.ProfFlags.typeSelector );
533        if (!b) return rtsFalse;
534    }
535    if (RtsFlags.ProfFlags.ccSelector) {
536        b = str_matches_selector( ((StgClosure *)p)->header.prof.ccs->cc->label,
537                                  RtsFlags.ProfFlags.ccSelector );
538        if (!b) return rtsFalse;
539    }
540    return rtsTrue;
541 }
542 #endif /* PROFILING */
543
544 /* -----------------------------------------------------------------------------
545  * Code to perform a heap census.
546  * -------------------------------------------------------------------------- */
547 static void
548 heapCensusChain( bdescr *bd )
549 {
550     StgPtr p;
551     StgInfoTable *info;
552     nat size;
553 #ifdef PROFILING
554     nat real_size;
555 #endif
556
557     for (; bd != NULL; bd = bd->link) {
558         p = bd->start;
559         while (p < bd->free) {
560             info = get_itbl((StgClosure *)p);
561             
562             switch (info->type) {
563
564             case CONSTR:
565             case BCO:
566             case FUN:
567             case THUNK:
568             case IND_PERM:
569             case IND_OLDGEN_PERM:
570             case CAF_BLACKHOLE:
571             case SE_CAF_BLACKHOLE:
572             case SE_BLACKHOLE:
573             case BLACKHOLE:
574             case BLACKHOLE_BQ:
575             case WEAK:
576             case FOREIGN:
577             case STABLE_NAME:
578             case MVAR:
579             case MUT_VAR:
580             case MUT_CONS:
581             case CONSTR_INTLIKE:
582             case CONSTR_CHARLIKE:
583             case FUN_1_0:
584             case FUN_0_1:
585             case FUN_1_1:
586             case FUN_0_2:
587             case FUN_2_0:
588             case THUNK_1_1:
589             case THUNK_0_2:
590             case THUNK_2_0:
591             case CONSTR_1_0:
592             case CONSTR_0_1:
593             case CONSTR_1_1:
594             case CONSTR_0_2:
595             case CONSTR_2_0:
596                 size = sizeW_fromITBL(info);
597                 break;
598                 
599             case THUNK_1_0:             /* ToDo - shouldn't be here */
600             case THUNK_0_1:             /* "  ditto  " */
601             case THUNK_SELECTOR:
602                 size = sizeofW(StgHeader) + MIN_UPD_SIZE;
603                 break;
604
605             case PAP:
606             case AP_UPD:
607                 size = pap_sizeW((StgPAP *)p);
608                 break;
609                 
610             case ARR_WORDS:
611                 size = arr_words_sizeW(stgCast(StgArrWords*,p));
612                 break;
613                 
614             case MUT_ARR_PTRS:
615             case MUT_ARR_PTRS_FROZEN:
616                 size = mut_arr_ptrs_sizeW((StgMutArrPtrs *)p);
617                 break;
618                 
619             case TSO:
620                 size = tso_sizeW((StgTSO *)p);
621                 break;
622                 
623             default:
624                 barf("heapCensus");
625             }
626             
627 #ifdef DEBUG_HEAP_PROF
628             switch (RtsFlags.ProfFlags.doHeapProfile) {
629             case HEAP_BY_INFOPTR:
630                 add_data((void *)(*p), size * sizeof(W_));
631                 break;
632             case HEAP_BY_CLOSURE_TYPE:
633                 closure_types[info->type] += size * sizeof(W_);
634                 break;
635             }
636 #endif
637             
638 #ifdef PROFILING
639             // subtract the profiling overhead
640             real_size = size - sizeofW(StgProfHeader);
641
642             if (closureSatisfiesConstraints((StgClosure*)p)) {
643                 switch (RtsFlags.ProfFlags.doHeapProfile) {
644                 case HEAP_BY_CCS:
645                     ((StgClosure *)p)->header.prof.ccs->mem_resid += real_size;
646                     break;
647                 case HEAP_BY_MOD:
648                     strToCtr(((StgClosure *)p)->header.prof.ccs->cc->module)
649                         ->mem_resid += real_size;
650                     break;
651                 case HEAP_BY_DESCR:
652                     strToCtr(get_itbl(((StgClosure *)p))->prof.closure_desc)->mem_resid 
653                         += real_size;
654                     break;
655                 case HEAP_BY_TYPE:
656                     strToCtr(get_itbl(((StgClosure *)p))->prof.closure_type)->mem_resid
657                         += real_size;
658                     break;
659                 default:
660                     barf("heapCensus; doHeapProfile");
661                 }
662             }
663 #endif
664             p += size;
665         }
666     }
667 }
668
669 void
670 heapCensus( void )
671 {
672   StgDouble time;
673   nat g, s;
674     
675 #ifdef DEBUG_HEAP_PROF
676   switch (RtsFlags.ProfFlags.doHeapProfile) {
677   case HEAP_BY_INFOPTR:
678     clear_table_data();
679     break;
680   case HEAP_BY_CLOSURE_TYPE:
681 #if 0
682 #   error fix me      
683     memset(closure_types, 0, N_CLOSURE_TYPES * sizeof(nat));
684 #endif
685     break;
686   default:
687     return;
688   }
689 #endif
690
691 #ifdef PROFILING
692   switch (RtsFlags.ProfFlags.doHeapProfile) {
693   case NO_HEAP_PROFILING:
694       return;
695   case HEAP_BY_CCS:
696       /* zero all the residency counters */
697       clearCCSResid(CCS_MAIN);
698       break;
699   case HEAP_BY_MOD:
700   case HEAP_BY_DESCR:
701   case HEAP_BY_TYPE:
702       clearCtrResid();
703       break;
704   default:
705       barf("heapCensus; doHeapProfile");
706   }
707 #endif
708
709   time = mut_user_time_during_GC();  
710   fprintf(hp_file, "BEGIN_SAMPLE %0.2f\n", time);
711
712   if (RtsFlags.GcFlags.generations == 1) {
713       heapCensusChain( g0s0->to_blocks );
714   } else {
715       for (g = 0; g < RtsFlags.GcFlags.generations; g++) {
716           for (s = 0; s < generations[g].n_steps; s++) {
717               heapCensusChain( generations[g].steps[s].blocks );
718           }
719       }
720   }
721
722 #ifdef DEBUG_HEAP_PROF
723   switch (RtsFlags.ProfFlags.doHeapProfile) {
724   case HEAP_BY_INFOPTR:
725       fprint_data(hp_file);
726       break;
727   case HEAP_BY_CLOSURE_TYPE:
728       fprint_closure_types(hp_file);
729       break;
730   }
731 #endif
732     
733 #ifdef PROFILING
734   switch (RtsFlags.ProfFlags.doHeapProfile) {
735   case HEAP_BY_CCS:
736       reportCCSResid(hp_file,CCS_MAIN);
737       break;
738   case HEAP_BY_MOD:
739   case HEAP_BY_DESCR:
740   case HEAP_BY_TYPE:
741       reportCtrResid(hp_file);
742       break;
743   default:
744       barf("heapCensus; doHeapProfile");
745   }
746 #endif
747
748   fprintf(hp_file, "END_SAMPLE %0.2f\n", time);
749 }    
750
751 #endif /* PROFILING || DEBUG_HEAP_PROF */
752