From c26856da8e344e7ec94d55f92ed49a09f04c5769 Mon Sep 17 00:00:00 2001 From: Chris Wilson Date: Mon, 13 Nov 2006 16:07:36 +0000 Subject: * Track memory leaks in allocations via the standard libraries, and avoid malloc/delete mismatches, by overriding standard new operator. * Added another global enable flag to memleak finder, which is used to mark the end of static allocations and the start of dynamic code, since the memory leak detection is done before cleanup of static objects. * Added a public guard class, to allow safe scoped disabling of memory leak detection. * Added InternalAllocGuard to protect against recursive loops when allocating memory inside the memory leak checker. (refs #3) --- lib/common/DebugMemLeakFinder.cpp | 139 ++++++++++++++++++++++++++++++++------ lib/common/MemLeakFinder.h | 16 ++++- 2 files changed, 133 insertions(+), 22 deletions(-) diff --git a/lib/common/DebugMemLeakFinder.cpp b/lib/common/DebugMemLeakFinder.cpp index 71665102..64ea0bd3 100644 --- a/lib/common/DebugMemLeakFinder.cpp +++ b/lib/common/DebugMemLeakFinder.cpp @@ -25,6 +25,7 @@ #include #include +static bool memleakfinder_initialised = false; bool memleakfinder_global_enable = false; typedef struct @@ -58,8 +59,38 @@ namespace size_t sNotLeaksPreNum = 0; } +void memleakfinder_init() +{ + ASSERT(!memleakfinder_initialised); + memleakfinder_initialised = true; +} + +MemLeakSuppressionGuard::MemLeakSuppressionGuard() +{ + ASSERT(memleakfinder_global_enable); + memleakfinder_global_enable = false; +} + +MemLeakSuppressionGuard::~MemLeakSuppressionGuard() +{ + ASSERT(!memleakfinder_global_enable); + memleakfinder_global_enable = true; +} + +// these functions may well allocate memory, which we don't want to track. +static int sInternalAllocDepth = 0; + +class InternalAllocGuard +{ + public: + InternalAllocGuard () { sInternalAllocDepth++; } + ~InternalAllocGuard() { sInternalAllocDepth--; } +}; + void memleakfinder_malloc_add_block(void *b, size_t size, const char *file, int line) { + InternalAllocGuard guard; + if(b != 0) { MallocBlockInfo i; @@ -75,11 +106,13 @@ void memleakfinder_malloc_add_block(void *b, size_t size, const char *file, int } } - void *memleakfinder_malloc(size_t size, const char *file, int line) { + InternalAllocGuard guard; + void *b = ::malloc(size); if(!memleakfinder_global_enable) return b; + if(!memleakfinder_initialised) return b; memleakfinder_malloc_add_block(b, size, file, line); @@ -89,7 +122,9 @@ void *memleakfinder_malloc(size_t size, const char *file, int line) void *memleakfinder_realloc(void *ptr, size_t size) { - if(!memleakfinder_global_enable) + InternalAllocGuard guard; + + if(!memleakfinder_global_enable || !memleakfinder_initialised) { return ::realloc(ptr, size); } @@ -133,7 +168,9 @@ void *memleakfinder_realloc(void *ptr, size_t size) void memleakfinder_free(void *ptr) { - if(memleakfinder_global_enable) + InternalAllocGuard guard; + + if(memleakfinder_global_enable && memleakfinder_initialised) { // Check it's been allocated std::map::iterator i(sMallocBlocks.find(ptr)); @@ -143,7 +180,7 @@ void memleakfinder_free(void *ptr) } else { - TRACE1("Block %x freed, but not known. Error? Or allocated in startup static allocation?\n", ptr); + TRACE1("Block %p freed, but not known. Error? Or allocated in startup static allocation?\n", ptr); } if(sTrackMallocInSection) @@ -160,24 +197,34 @@ void memleakfinder_free(void *ptr) void memleakfinder_notaleak_insert_pre() { + InternalAllocGuard guard; + if(!memleakfinder_global_enable) return; + if(!memleakfinder_initialised) return; + for(size_t l = 0; l < sNotLeaksPreNum; l++) { sNotLeaks.insert(sNotLeaksPre[l]); } + sNotLeaksPreNum = 0; } bool is_leak(void *ptr) { + InternalAllocGuard guard; + + ASSERT(memleakfinder_initialised); memleakfinder_notaleak_insert_pre(); return sNotLeaks.find(ptr) == sNotLeaks.end(); } void memleakfinder_notaleak(void *ptr) { + InternalAllocGuard guard; + memleakfinder_notaleak_insert_pre(); - if(memleakfinder_global_enable) + if(memleakfinder_global_enable && memleakfinder_initialised) { sNotLeaks.insert(ptr); } @@ -206,6 +253,9 @@ void memleakfinder_notaleak(void *ptr) // start monitoring a section of code void memleakfinder_startsectionmonitor() { + InternalAllocGuard guard; + + ASSERT(memleakfinder_initialised); sTrackMallocInSection = true; sSectionMallocBlocks.clear(); sTrackObjectsInSection = true; @@ -215,6 +265,10 @@ void memleakfinder_startsectionmonitor() // trace all blocks allocated and still allocated since memleakfinder_startsectionmonitor() called void memleakfinder_traceblocksinsection() { + InternalAllocGuard guard; + + ASSERT(memleakfinder_initialised); + std::set::iterator s(sSectionMallocBlocks.begin()); for(; s != sSectionMallocBlocks.end(); ++s) { @@ -225,17 +279,21 @@ void memleakfinder_traceblocksinsection() } else { - TRACE4("Block 0x%08p size %d allocated at %s:%d\n", i->first, i->second.size, i->second.file, i->second.line); + TRACE4("Block %p size %d allocated at %s:%d\n", i->first, i->second.size, i->second.file, i->second.line); } } for(std::map::const_iterator i(sSectionObjectBlocks.begin()); i != sSectionObjectBlocks.end(); ++i) { - TRACE5("Object%s 0x%08p size %d allocated at %s:%d\n", i->second.array?" []":"", i->first, i->second.size, i->second.file, i->second.line); + TRACE5("Object%s %p size %d allocated at %s:%d\n", i->second.array?" []":"", i->first, i->second.size, i->second.file, i->second.line); } } int memleakfinder_numleaks() { + InternalAllocGuard guard; + + ASSERT(memleakfinder_initialised); + int n = 0; for(std::map::const_iterator i(sMallocBlocks.begin()); i != sMallocBlocks.end(); ++i) @@ -245,6 +303,7 @@ int memleakfinder_numleaks() for(std::map::const_iterator i(sObjectBlocks.begin()); i != sObjectBlocks.end(); ++i) { + const ObjectInfo& rInfo = i->second; if(is_leak(i->first)) ++n; } @@ -253,6 +312,8 @@ int memleakfinder_numleaks() void memleakfinder_reportleaks_file(FILE *file) { + InternalAllocGuard guard; + for(std::map::const_iterator i(sMallocBlocks.begin()); i != sMallocBlocks.end(); ++i) { if(is_leak(i->first)) ::fprintf(file, "Block 0x%p size %d allocated at %s:%d\n", i->first, i->second.size, i->second.file, i->second.line); @@ -265,12 +326,16 @@ void memleakfinder_reportleaks_file(FILE *file) void memleakfinder_reportleaks() { + InternalAllocGuard guard; + // report to stdout memleakfinder_reportleaks_file(stdout); } void memleakfinder_reportleaks_appendfile(const char *filename, const char *markertext) { + InternalAllocGuard guard; + FILE *file = ::fopen(filename, "a"); if(file != 0) { @@ -317,7 +382,10 @@ void memleakfinder_setup_exit_report(const char *filename, const char *markertex void add_object_block(void *block, size_t size, const char *file, int line, bool array) { + InternalAllocGuard guard; + if(!memleakfinder_global_enable) return; + if(!memleakfinder_initialised) return; if(block != 0) { @@ -337,7 +405,10 @@ void add_object_block(void *block, size_t size, const char *file, int line, bool void remove_object_block(void *block) { + InternalAllocGuard guard; + if(!memleakfinder_global_enable) return; + if(!memleakfinder_initialised) return; std::map::iterator i(sObjectBlocks.find(block)); if(i != sObjectBlocks.end()) @@ -357,34 +428,64 @@ void remove_object_block(void *block) // If it's not in the list, just ignore it, as lots of stuff goes this way... } -void *operator new(size_t size, const char *file, int line) +static void *internal_new(size_t size, const char *file, int line) { - void *r = ::malloc(size); - add_object_block(r, size, file, line, false); - //TRACE4("new(), %d, %s, %d, %08x\n", size, file, line, r); + void *r; + + { + InternalAllocGuard guard; + r = ::malloc(size); + } + + if (sInternalAllocDepth == 0) + { + InternalAllocGuard guard; + add_object_block(r, size, file, line, false); + //TRACE4("new(), %d, %s, %d, %08x\n", size, file, line, r); + } + return r; } +void *operator new(size_t size, const char *file, int line) +{ + return internal_new(size, file, line); +} + void *operator new[](size_t size, const char *file, int line) { - void *r = ::malloc(size); - add_object_block(r, size, file, line, true); - //TRACE4("new[](), %d, %s, %d, %08x\n", size, file, line, r); - return r; + return internal_new(size, file, line); } -void operator delete[](void *ptr) throw () +// where there is no doctor... need to override standard new() too +// http://www.relisoft.com/book/tech/9new.html +void *operator new(size_t size) +{ + return internal_new(size, "standard libraries", 0); +} + +void *operator new[](size_t size) +{ + return internal_new(size, "standard libraries", 0); +} + +void internal_delete(void *ptr) { + InternalAllocGuard guard; + ::free(ptr); remove_object_block(ptr); //TRACE1("delete[]() called, %08x\n", ptr); } +void operator delete[](void *ptr) throw () +{ + internal_delete(ptr); +} + void operator delete(void *ptr) throw () { - ::free(ptr); - remove_object_block(ptr); - //TRACE1("delete() called, %08x\n", ptr); + internal_delete(ptr); } #endif // NDEBUG diff --git a/lib/common/MemLeakFinder.h b/lib/common/MemLeakFinder.h index f5887dac..f47a38aa 100644 --- a/lib/common/MemLeakFinder.h +++ b/lib/common/MemLeakFinder.h @@ -20,6 +20,13 @@ // global enable flag extern bool memleakfinder_global_enable; +class MemLeakSuppressionGuard +{ + public: + MemLeakSuppressionGuard(); + ~MemLeakSuppressionGuard(); +}; + extern "C" { void *memleakfinder_malloc(size_t size, const char *file, int line); @@ -27,6 +34,8 @@ extern "C" void memleakfinder_free(void *ptr); } +void memleakfinder_init(); + int memleakfinder_numleaks(); void memleakfinder_reportleaks(); @@ -41,10 +50,12 @@ void memleakfinder_traceblocksinsection(); void memleakfinder_notaleak(void *ptr); -void *operator new(size_t size, const char *file, int line); +void *operator new (size_t size, const char *file, int line); void *operator new[](size_t size, const char *file, int line); +void *operator new (size_t size); +void *operator new[](size_t size); -void operator delete(void *ptr) throw (); +void operator delete (void *ptr) throw (); void operator delete[](void *ptr) throw (); // define the malloc functions now, if required @@ -55,6 +66,5 @@ void operator delete[](void *ptr) throw (); #define MEMLEAKFINDER_MALLOC_MONITORING_DEFINED #endif - #endif // MEMLEAKFINDER__H -- cgit v1.2.3