#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 -------------------------- */
}
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
#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
#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++ )
{
#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)
-------------------------------------------------------------------------- */
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.
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)
{
leak = live_blocks + free_blocks != mblocks_allocated * BLOCKS_PER_MBLOCK;
- ASSERT(n_alloc_blocks == live_blocks);
-
if (show || leak)
{
if (leak) {
mblocks_allocated * BLOCKS_PER_MBLOCK, mblocks_allocated);
}
}
+
+ if (leak) {
+ debugBelch("\n");
+ findMemoryLeak();
+ }
+ ASSERT(n_alloc_blocks == live_blocks);
+ ASSERT(!leak);
}