1 /* -----------------------------------------------------------------------------
2 * $Id: ProfHeap.c,v 1.26 2001/11/22 14:25:12 simonmar Exp $
4 * (c) The GHC Team, 1998-2000
6 * Support for heap profiling
8 * ---------------------------------------------------------------------------*/
10 #if defined(DEBUG) && !defined(PROFILING)
11 #define DEBUG_HEAP_PROF
13 #undef DEBUG_HEAP_PROF
16 #if defined(PROFILING) || defined(DEBUG_HEAP_PROF)
18 #include "PosixSource.h"
22 #include "Profiling.h"
28 #include "RetainerProfile.h"
29 #include "LdvProfile.h"
31 #ifdef DEBUG_HEAP_PROF
33 static void initSymbolHash(void);
34 static void clear_table_data(void);
35 static void fprint_data(FILE *fp);
38 /* -----------------------------------------------------------------------------
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.
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
54 * -------------------------------------------------------------------------- */
59 unsigned long mem_resid;
61 struct _ctr *next_bucket;
64 /* Linked list of all existing ctr structs */
67 /* Hash table mapping (char *) -> (struct _ctr) */
68 HashTable *str_to_ctr;
70 /* Hash table mapping hash_t (hashed string) -> (struct _ctr) */
71 HashTable *hashstr_to_ctrs;
74 initHashTables( void )
76 str_to_ctr = allocHashTable();
77 hashstr_to_ctrs = allocHashTable();
82 strToCtr(const char *str)
86 ctr = lookupHashTable( str_to_ctr, (W_)str );
88 if (ctr != NULL) { return ctr; }
91 hash_t str_hash = hash_str((char *)str);
94 ctr = lookupHashTable( hashstr_to_ctrs, (W_)str_hash );
97 for (; ctr != NULL; prev = ctr, ctr = ctr->next_bucket ) {
98 if (!strcmp(ctr->str, str)) {
99 insertHashTable( str_to_ctr, (W_)str, ctr );
101 fprintf(stderr,"strToCtr: existing ctr for `%s'\n",str);
107 ctr = stgMallocBytes(sizeof(prof_ctr), "strToCtr");
110 ctr->next_bucket = NULL;
111 ctr->next = all_ctrs;
115 fprintf(stderr,"strToCtr: new ctr for `%s'\n",str);
119 prev->next_bucket = ctr;
121 insertHashTable( hashstr_to_ctrs, str_hash, ctr );
123 insertHashTable( str_to_ctr, (W_)str, ctr);
129 clearCtrResid( void )
133 for (ctr = all_ctrs; ctr != NULL; ctr = ctr->next) {
139 reportCtrResid(FILE *fp)
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_));
149 #endif /* PROFILING */
151 /* -------------------------------------------------------------------------- */
153 #ifdef DEBUG_HEAP_PROF
156 void initProfiling1( void )
160 void initProfiling2( void )
165 void endProfiling( void )
169 #endif /* DEBUG_HEAP_PROF */
172 initHeapProfiling(void)
174 if (! RtsFlags.ProfFlags.doHeapProfile) {
178 fprintf(hp_file, "JOB \"%s", prog_argv[0]);
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");
190 #endif /* PROFILING */
192 fprintf(hp_file, "\"\n" );
194 fprintf(hp_file, "DATE \"%s\"\n", time_str());
196 fprintf(hp_file, "SAMPLE_UNIT \"seconds\"\n");
197 fprintf(hp_file, "VALUE_UNIT \"bytes\"\n");
199 fprintf(hp_file, "BEGIN_SAMPLE 0.00\n");
200 fprintf(hp_file, "END_SAMPLE 0.00\n");
202 #ifdef DEBUG_HEAP_PROF
203 DEBUG_LoadSymbols(prog_argv[0]);
215 endHeapProfiling(void)
219 if (! RtsFlags.ProfFlags.doHeapProfile) {
224 switch (RtsFlags.ProfFlags.doHeapProfile) {
225 case HEAP_BY_RETAINER:
226 endRetainerProfiling();
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);
240 #ifdef DEBUG_HEAP_PROF
241 /* -----------------------------------------------------------------------------
242 Hash table for symbols.
243 -------------------------------------------------------------------------- */
251 #define SYMBOL_HASH_SIZE 0x3fff
253 symbol_info symbol_hash[SYMBOL_HASH_SIZE];
258 return ((W_)ptr)>>4 & 0x3fff;
266 for (i=0; i < SYMBOL_HASH_SIZE; i++) {
267 symbol_hash[i].ptr = NULL;
272 lookup_symbol(void *addr)
274 nat orig_bucket = hash(addr);
277 bucket = orig_bucket;
278 while (bucket < SYMBOL_HASH_SIZE && symbol_hash[bucket].ptr != NULL) {
279 if (symbol_hash[bucket].ptr == addr) {
284 if (bucket == SYMBOL_HASH_SIZE) {
286 while (bucket < orig_bucket && symbol_hash[bucket].ptr != NULL) {
287 if (symbol_hash[bucket].ptr == addr) {
292 if (bucket == orig_bucket) {
293 barf("out of symbol table space");
297 symbol_hash[bucket].ptr = addr;
298 lookupGHCName(addr,&symbol_hash[bucket].name);
299 symbol_hash[bucket].data = 0;
304 clear_table_data(void)
308 for (i = 0; i < SYMBOL_HASH_SIZE; i++) {
309 symbol_hash[i].data = 0;
314 fprint_data(FILE *fp)
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);
325 add_data(void *addr, nat data)
327 symbol_hash[lookup_symbol(addr)].data += data;
330 /* -----------------------------------------------------------------------------
331 Closure Type Profiling;
333 PROBABLY TOTALLY OUT OF DATE -- ToDo (SDM)
334 -------------------------------------------------------------------------- */
336 static nat closure_types[N_CLOSURE_TYPES];
338 static char *type_names[] = {
344 , "CONSTR_NOCAF_STATIC"
382 , "MUT_ARR_PTRS_FROZEN"
397 fprint_closure_types(FILE *fp)
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]);
408 #endif /* DEBUG_HEAP_PROF */
413 clearCCSResid(CostCentreStack *ccs)
419 for (i = ccs->indexTable; i != 0; i = i->next) {
421 clearCCSResid(i->ccs);
427 fprint_ccs(FILE *fp, CostCentreStack *ccs, nat max_length)
429 char buf[max_length+1];
434 // MAIN on its own gets printed as "MAIN", otherwise we ignore MAIN.
435 if (ccs == CCS_MAIN) {
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) {
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);
451 if (ccs->prevStack != NULL && ccs->prevStack != CCS_MAIN) {
456 written = snprintf(buf+next_offset,
457 (int)max_length-3-(int)next_offset,
458 template, ccs->cc->label);
461 if (next_offset+written >= max_length-4) {
462 sprintf(buf+max_length-4, "...");
465 next_offset += written;
468 fprintf(fp, "%s", buf);
472 reportCCSResid(FILE *fp, CostCentreStack *ccs)
476 if (ccs->mem_resid != 0) {
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_));
483 for (i = ccs->indexTable; i != 0; i = i->next) {
485 reportCCSResid(fp,i->ccs);
491 str_matches_selector( char* str, char* sel )
494 // fprintf(stderr, "str_matches_selector %s %s\n", str, sel);
496 // Compare str against wherever we've got to in sel.
498 while (*p != '\0' && *sel != ',' && *sel != '\0' && *p == *sel) {
501 // Match if all of str used and have reached the end of a sel fragment.
502 if (*p == '\0' && (*sel == ',' || *sel == '\0'))
505 // No match. Advance sel to the start of the next elem.
506 while (*sel != ',' && *sel != '\0') sel++;
507 if (*sel == ',') sel++;
509 /* Run out of sel ?? */
510 if (*sel == '\0') return rtsFalse;
514 // Figure out whether a closure should be counted in this census, by
515 // testing against all the specified constraints.
517 closureSatisfiesConstraints( StgClosure* p )
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;
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;
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;
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;
542 #endif /* PROFILING */
544 /* -----------------------------------------------------------------------------
545 * Code to perform a heap census.
546 * -------------------------------------------------------------------------- */
548 heapCensusChain( bdescr *bd )
557 for (; bd != NULL; bd = bd->link) {
559 while (p < bd->free) {
560 info = get_itbl((StgClosure *)p);
562 switch (info->type) {
569 case IND_OLDGEN_PERM:
571 case SE_CAF_BLACKHOLE:
582 case CONSTR_CHARLIKE:
596 size = sizeW_fromITBL(info);
599 case THUNK_1_0: /* ToDo - shouldn't be here */
600 case THUNK_0_1: /* " ditto " */
602 size = sizeofW(StgHeader) + MIN_UPD_SIZE;
607 size = pap_sizeW((StgPAP *)p);
611 size = arr_words_sizeW(stgCast(StgArrWords*,p));
615 case MUT_ARR_PTRS_FROZEN:
616 size = mut_arr_ptrs_sizeW((StgMutArrPtrs *)p);
620 size = tso_sizeW((StgTSO *)p);
627 #ifdef DEBUG_HEAP_PROF
628 switch (RtsFlags.ProfFlags.doHeapProfile) {
629 case HEAP_BY_INFOPTR:
630 add_data((void *)(*p), size * sizeof(W_));
632 case HEAP_BY_CLOSURE_TYPE:
633 closure_types[info->type] += size * sizeof(W_);
639 // subtract the profiling overhead
640 real_size = size - sizeofW(StgProfHeader);
642 if (closureSatisfiesConstraints((StgClosure*)p)) {
643 switch (RtsFlags.ProfFlags.doHeapProfile) {
645 ((StgClosure *)p)->header.prof.ccs->mem_resid += real_size;
648 strToCtr(((StgClosure *)p)->header.prof.ccs->cc->module)
649 ->mem_resid += real_size;
652 strToCtr(get_itbl(((StgClosure *)p))->prof.closure_desc)->mem_resid
656 strToCtr(get_itbl(((StgClosure *)p))->prof.closure_type)->mem_resid
660 barf("heapCensus; doHeapProfile");
675 #ifdef DEBUG_HEAP_PROF
676 switch (RtsFlags.ProfFlags.doHeapProfile) {
677 case HEAP_BY_INFOPTR:
680 case HEAP_BY_CLOSURE_TYPE:
683 memset(closure_types, 0, N_CLOSURE_TYPES * sizeof(nat));
692 switch (RtsFlags.ProfFlags.doHeapProfile) {
693 case NO_HEAP_PROFILING:
696 /* zero all the residency counters */
697 clearCCSResid(CCS_MAIN);
705 barf("heapCensus; doHeapProfile");
709 time = mut_user_time_during_GC();
710 fprintf(hp_file, "BEGIN_SAMPLE %0.2f\n", time);
712 if (RtsFlags.GcFlags.generations == 1) {
713 heapCensusChain( g0s0->to_blocks );
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 );
722 #ifdef DEBUG_HEAP_PROF
723 switch (RtsFlags.ProfFlags.doHeapProfile) {
724 case HEAP_BY_INFOPTR:
725 fprint_data(hp_file);
727 case HEAP_BY_CLOSURE_TYPE:
728 fprint_closure_types(hp_file);
734 switch (RtsFlags.ProfFlags.doHeapProfile) {
736 reportCCSResid(hp_file,CCS_MAIN);
741 reportCtrResid(hp_file);
744 barf("heapCensus; doHeapProfile");
748 fprintf(hp_file, "END_SAMPLE %0.2f\n", time);
751 #endif /* PROFILING || DEBUG_HEAP_PROF */