From: Simon Marlow Date: Tue, 9 Sep 2008 14:51:22 +0000 (+0000) Subject: when a memory leak is detected, report which blocks are unreachable X-Git-Tag: 2008-09-12~36 X-Git-Url: http://git.megacz.com/?p=ghc-hetmet.git;a=commitdiff_plain;h=43a120b706a5eece6624ca4907af89fc9a480c5e when a memory leak is detected, report which blocks are unreachable --- diff --git a/includes/Block.h b/includes/Block.h index 3d7a5c8..28e0374 100644 --- a/includes/Block.h +++ b/includes/Block.h @@ -92,7 +92,8 @@ typedef struct bdescr_ { #define BF_EXEC 32 /* Block contains only a small amount of live data */ #define BF_FRAGMENTED 64 - +/* we know about this block (for finding leaks) */ +#define BF_KNOWN 128 /* Finding the block descriptor for a given block -------------------------- */ diff --git a/rts/sm/BlockAlloc.c b/rts/sm/BlockAlloc.c index 81baf6c..daf9fb0 100644 --- a/rts/sm/BlockAlloc.c +++ b/rts/sm/BlockAlloc.c @@ -727,4 +727,36 @@ countFreeList(void) } return total_blocks; } + +void +markBlocks (bdescr *bd) +{ + for (; bd != NULL; bd = bd->link) { + bd->flags |= BF_KNOWN; + } +} + +void +reportUnmarkedBlocks (void) +{ + void *mblock; + bdescr *bd; + + debugBelch("Unreachable blocks:\n"); + for (mblock = getFirstMBlock(); mblock != NULL; + mblock = getNextMBlock(mblock)) { + for (bd = FIRST_BDESCR(mblock); bd <= LAST_BDESCR(mblock); ) { + if (!(bd->flags & BF_KNOWN) && bd->free != (P_)-1) { + debugBelch(" %p\n",bd); + } + if (bd->blocks >= BLOCKS_PER_MBLOCK) { + mblock += (BLOCKS_TO_MBLOCKS(bd->blocks) - 1) * MBLOCK_SIZE; + break; + } else { + bd += bd->blocks; + } + } + } +} + #endif diff --git a/rts/sm/BlockAlloc.h b/rts/sm/BlockAlloc.h index 594135a..776df9b 100644 --- a/rts/sm/BlockAlloc.h +++ b/rts/sm/BlockAlloc.h @@ -14,6 +14,8 @@ #ifdef DEBUG extern void checkFreeListSanity(void); nat countFreeList(void); +void markBlocks (bdescr *bd); +void reportUnmarkedBlocks (void); #endif extern lnat n_alloc_blocks; // currently allocated blocks diff --git a/rts/sm/MBlock.c b/rts/sm/MBlock.c index f302f84..28aa7b0 100644 --- a/rts/sm/MBlock.c +++ b/rts/sm/MBlock.c @@ -34,13 +34,13 @@ StgWord8 mblock_map[MBLOCK_MAP_SIZE]; // initially all zeros #elif SIZEOF_VOID_P == 8 static MBlockMap dummy_mblock_map; MBlockMap *mblock_cache = &dummy_mblock_map; -int mblock_map_count = 0; +nat mblock_map_count = 0; MBlockMap **mblock_maps = NULL; static MBlockMap * findMBlockMap(void *p) { - int i; + nat i; StgWord32 hi = (StgWord32) (((StgWord)p) >> 32); for( i = 0; i < mblock_map_count; i++ ) { @@ -86,6 +86,95 @@ markHeapAlloced(void *p) #endif } +/* ---------------------------------------------------------------------------- + Debugging code for traversing the allocated MBlocks + + This is used for searching for lost blocks when a memory leak is + detected; see Blocks.c:findUnmarkedBlock(). + ------------------------------------------------------------------------ */ + +#ifdef DEBUG + +#if SIZEOF_VOID_P == 4 + +STATIC_INLINE +void * mapEntryToMBlock(nat i) +{ + return (void *)((StgWord)i << MBLOCK_SHIFT); +} + +void * getFirstMBlock(void) +{ + nat i; + + for (i = 0; i < MBLOCK_MAP_SIZE; i++) { + if (mblock_map[i]) return mapEntryToMBlock(i); + } + return NULL; +} + +void * getNextMBlock(void *mblock) +{ + nat i; + + for (i = MBLOCK_MAP_ENTRY(mblock) + 1; i < MBLOCK_MAP_SIZE; i++) { + if (mblock_map[i]) return mapEntryToMBlock(i); + } + return NULL; +} + +#elif SIZEOF_VOID_P == 8 + +STATIC_INLINE +void * mapEntryToMBlock(MBlockMap *map, nat i) +{ + return (void *)(((StgWord)map->addrHigh32) << 32) + + ((StgWord)i << MBLOCK_SHIFT); +} + +void * getFirstMBlock(void) +{ + MBlockMap *map; + nat i, j; + + for (j = 0; j < mblock_map_count; j++) { + map = mblock_maps[j]; + for (i = 0; i < MBLOCK_MAP_SIZE; i++) { + if (map->mblocks[i]) return mapEntryToMBlock(map,i); + } + } + return NULL; +} + +void * getNextMBlock(void *mblock) +{ + MBlockMap *map; + nat i, j; + + for (j = 0; j < mblock_map_count; j++) { + map = mblock_maps[j]; + if (map->addrHigh32 == (StgWord)mblock >> 32) break; + } + if (j == mblock_map_count) return NULL; + + for (; j < mblock_map_count; j++) { + map = mblock_maps[j]; + if (map->addrHigh32 == (StgWord)mblock >> 32) { + i = MBLOCK_MAP_ENTRY(mblock) + 1; + } else { + i = 0; + } + for (; i < MBLOCK_MAP_SIZE; i++) { + if (map->mblocks[i]) return mapEntryToMBlock(map,i); + } + } + return NULL; +} + +#endif // SIZEOF_VOID_P + +#endif // DEBUG + /* ----------------------------------------------------------------------------- Allocate new mblock(s) -------------------------------------------------------------------------- */ diff --git a/rts/sm/MBlock.h b/rts/sm/MBlock.h index 17ade51..14244dc 100644 --- a/rts/sm/MBlock.h +++ b/rts/sm/MBlock.h @@ -16,6 +16,11 @@ extern void * getMBlock(void); extern void * getMBlocks(nat n); extern void freeAllMBlocks(void); +#ifdef DEBUG +extern void *getFirstMBlock(void); +extern void *getNextMBlock(void *mblock); +#endif + /* ----------------------------------------------------------------------------- The HEAP_ALLOCED() test. diff --git a/rts/sm/Storage.c b/rts/sm/Storage.c index 6cccf34..a6134c6 100644 --- a/rts/sm/Storage.c +++ b/rts/sm/Storage.c @@ -1270,6 +1270,51 @@ stepBlocks (step *stp) countAllocdBlocks(stp->large_objects); } +// If memInventory() calculates that we have a memory leak, this +// function will try to find the block(s) that are leaking by marking +// all the ones that we know about, and search through memory to find +// blocks that are not marked. In the debugger this can help to give +// us a clue about what kind of block leaked. In the future we might +// annotate blocks with their allocation site to give more helpful +// info. +static void +findMemoryLeak (void) +{ + nat g, s, i; + for (g = 0; g < RtsFlags.GcFlags.generations; g++) { + for (i = 0; i < n_capabilities; i++) { + markBlocks(capabilities[i].mut_lists[g]); + } + markBlocks(generations[g].mut_list); + for (s = 0; s < generations[g].n_steps; s++) { + markBlocks(generations[g].steps[s].blocks); + markBlocks(generations[g].steps[s].large_objects); + } + } + + for (i = 0; i < n_nurseries; i++) { + markBlocks(nurseries[i].blocks); + markBlocks(nurseries[i].large_objects); + } + +#ifdef PROFILING + // TODO: + // if (RtsFlags.ProfFlags.doHeapProfile == HEAP_BY_RETAINER) { + // markRetainerBlocks(); + // } +#endif + + // count the blocks allocated by the arena allocator + // TODO: + // markArenaBlocks(); + + // count the blocks containing executable memory + markBlocks(exec_block); + + reportUnmarkedBlocks(); +} + + void memInventory (rtsBool show) { @@ -1327,8 +1372,6 @@ memInventory (rtsBool show) leak = live_blocks + free_blocks != mblocks_allocated * BLOCKS_PER_MBLOCK; - ASSERT(n_alloc_blocks == live_blocks); - if (show || leak) { if (leak) { @@ -1357,6 +1400,13 @@ memInventory (rtsBool show) mblocks_allocated * BLOCKS_PER_MBLOCK, mblocks_allocated); } } + + if (leak) { + debugBelch("\n"); + findMemoryLeak(); + } + ASSERT(n_alloc_blocks == live_blocks); + ASSERT(!leak); }