// distribution boxbackup-0.10 (svn version: 494) // // Copyright (c) 2003 - 2006 // Ben Summers and contributors. All rights reserved. // // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions // are met: // 1. Redistributions of source code must retain the above copyright // notice, this list of conditions and the following disclaimer. // 2. Redistributions in binary form must reproduce the above copyright // notice, this list of conditions and the following disclaimer in the // documentation and/or other materials provided with the distribution. // 3. All use of this software and associated advertising materials must // display the following acknowledgment: // This product includes software developed by Ben Summers. // 4. The names of the Authors may not be used to endorse or promote // products derived from this software without specific prior written // permission. // // [Where legally impermissible the Authors do not disclaim liability for // direct physical injury or death caused solely by defects in the software // unless it is modified by a third party.] // // THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR // IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED // WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE // DISCLAIMED. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY DIRECT, // INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES // (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR // SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) // HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, // STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN // ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE // POSSIBILITY OF SUCH DAMAGE. // // // // -------------------------------------------------------------------------- // // File // Name: MemLeakFinder.cpp // Purpose: Memory leak finder // Created: 12/1/04 // // -------------------------------------------------------------------------- #ifndef NDEBUG #include "Box.h" #undef malloc #undef realloc #undef free #ifdef HAVE_UNISTD_H #include #endif #include #include #include #include bool memleakfinder_global_enable = false; typedef struct { size_t size; const char *file; int line; } MallocBlockInfo; typedef struct { size_t size; const char *file; int line; bool array; } ObjectInfo; namespace { static std::map sMallocBlocks; static std::map sObjectBlocks; static bool sTrackMallocInSection = false; static std::set sSectionMallocBlocks; static bool sTrackObjectsInSection = false; static std::map sSectionObjectBlocks; static std::set sNotLeaks; void *sNotLeaksPre[1024]; int sNotLeaksPreNum = 0; } void memleakfinder_malloc_add_block(void *b, size_t size, const char *file, int line) { if(b != 0) { MallocBlockInfo i; i.size = size; i.file = file; i.line = line; sMallocBlocks[b] = i; if(sTrackMallocInSection) { sSectionMallocBlocks.insert(b); } } } void *memleakfinder_malloc(size_t size, const char *file, int line) { void *b = ::malloc(size); if(!memleakfinder_global_enable) return b; memleakfinder_malloc_add_block(b, size, file, line); //TRACE4("malloc(), %d, %s, %d, %08x\n", size, file, line, b); return b; } void *memleakfinder_realloc(void *ptr, size_t size) { if(!memleakfinder_global_enable) { return ::realloc(ptr, size); } // Check it's been allocated std::map::iterator i(sMallocBlocks.find(ptr)); if(ptr && i == sMallocBlocks.end()) { TRACE1("Block %x realloc(), but not in list. Error? Or allocated in startup static objects?\n", ptr); } void *b = ::realloc(ptr, size); if(ptr && i!=sMallocBlocks.end()) { // Worked? if(b != 0) { // Update map MallocBlockInfo inf = i->second; inf.size = size; sMallocBlocks.erase(i); sMallocBlocks[b] = inf; if(sTrackMallocInSection) { std::set::iterator si(sSectionMallocBlocks.find(ptr)); if(si != sSectionMallocBlocks.end()) sSectionMallocBlocks.erase(si); sSectionMallocBlocks.insert(b); } } } else { memleakfinder_malloc_add_block(b, size, "FOUND-IN-REALLOC", 0); } //TRACE3("realloc(), %d, %08x->%08x\n", size, ptr, b); return b; } void memleakfinder_free(void *ptr) { if(memleakfinder_global_enable) { // Check it's been allocated std::map::iterator i(sMallocBlocks.find(ptr)); if(i != sMallocBlocks.end()) { sMallocBlocks.erase(i); } else { TRACE1("Block %x freed, but not known. Error? Or allocated in startup static allocation?\n", ptr); } if(sTrackMallocInSection) { std::set::iterator si(sSectionMallocBlocks.find(ptr)); if(si != sSectionMallocBlocks.end()) sSectionMallocBlocks.erase(si); } } //TRACE1("free(), %08x\n", ptr); ::free(ptr); } void memleakfinder_notaleak_insert_pre() { if(!memleakfinder_global_enable) return; for(int l = 0; l < sNotLeaksPreNum; l++) { sNotLeaks.insert(sNotLeaksPre[l]); } sNotLeaksPreNum = 0; } bool is_leak(void *ptr) { memleakfinder_notaleak_insert_pre(); return sNotLeaks.find(ptr) == sNotLeaks.end(); } void memleakfinder_notaleak(void *ptr) { memleakfinder_notaleak_insert_pre(); if(memleakfinder_global_enable) { sNotLeaks.insert(ptr); } else { sNotLeaksPre[sNotLeaksPreNum++] = ptr; } /* { std::map::iterator i(sMallocBlocks.find(ptr)); if(i != sMallocBlocks.end()) sMallocBlocks.erase(i); } { std::set::iterator si(sSectionMallocBlocks.find(ptr)); if(si != sSectionMallocBlocks.end()) sSectionMallocBlocks.erase(si); } { std::map::iterator i(sObjectBlocks.find(ptr)); if(i != sObjectBlocks.end()) sObjectBlocks.erase(i); }*/ } // start monitoring a section of code void memleakfinder_startsectionmonitor() { sTrackMallocInSection = true; sSectionMallocBlocks.clear(); sTrackObjectsInSection = true; sSectionObjectBlocks.clear(); } // trace all blocks allocated and still allocated since memleakfinder_startsectionmonitor() called void memleakfinder_traceblocksinsection() { std::set::iterator s(sSectionMallocBlocks.begin()); for(; s != sSectionMallocBlocks.end(); ++s) { std::map::const_iterator i(sMallocBlocks.find(*s)); if(i == sMallocBlocks.end()) { TRACE0("Logical error in section block finding\n"); } else { TRACE4("Block 0x%08p 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); } } int memleakfinder_numleaks() { int n = 0; for(std::map::const_iterator i(sMallocBlocks.begin()); i != sMallocBlocks.end(); ++i) { if(is_leak(i->first)) ++n; } for(std::map::const_iterator i(sObjectBlocks.begin()); i != sObjectBlocks.end(); ++i) { if(is_leak(i->first)) ++n; } return n; } void memleakfinder_reportleaks_file(FILE *file) { for(std::map::const_iterator i(sMallocBlocks.begin()); i != sMallocBlocks.end(); ++i) { if(is_leak(i->first)) ::fprintf(file, "Block 0x%08p size %d allocated at %s:%d\n", i->first, i->second.size, i->second.file, i->second.line); } for(std::map::const_iterator i(sObjectBlocks.begin()); i != sObjectBlocks.end(); ++i) { if(is_leak(i->first)) ::fprintf(file, "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); } } void memleakfinder_reportleaks() { // report to stdout memleakfinder_reportleaks_file(stdout); } void memleakfinder_reportleaks_appendfile(const char *filename, const char *markertext) { FILE *file = ::fopen(filename, "a"); if(file != 0) { if(memleakfinder_numleaks() > 0) { #ifdef HAVE_GETPID fprintf(file, "MEMORY LEAKS FROM PROCESS %d (%s)\n", getpid(), markertext); #else fprintf(file, "MEMORY LEAKS (%s)\n", markertext); #endif memleakfinder_reportleaks_file(file); } ::fclose(file); } else { printf("WARNING: Couldn't open memory leak results file %s for appending\n", filename); } } static char atexit_filename[512]; static char atexit_markertext[512]; static bool atexit_registered = false; void memleakfinder_atexit() { memleakfinder_reportleaks_appendfile(atexit_filename, atexit_markertext); } void memleakfinder_setup_exit_report(const char *filename, const char *markertext) { ::strcpy(atexit_filename, filename); ::strcpy(atexit_markertext, markertext); if(!atexit_registered) { atexit(memleakfinder_atexit); atexit_registered = true; } } void add_object_block(void *block, size_t size, const char *file, int line, bool array) { if(!memleakfinder_global_enable) return; if(block != 0) { ObjectInfo i; i.size = size; i.file = file; i.line = line; i.array = array; sObjectBlocks[block] = i; if(sTrackObjectsInSection) { sSectionObjectBlocks[block] = i; } } } void remove_object_block(void *block) { if(!memleakfinder_global_enable) return; std::map::iterator i(sObjectBlocks.find(block)); if(i != sObjectBlocks.end()) { sObjectBlocks.erase(i); } if(sTrackObjectsInSection) { std::map::iterator i(sSectionObjectBlocks.find(block)); if(i != sSectionObjectBlocks.end()) { sSectionObjectBlocks.erase(i); } } // 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) { void *r = ::malloc(size); 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) { 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; } void operator delete[](void *ptr) throw () { ::free(ptr); remove_object_block(ptr); //TRACE1("delete[]() called, %08x\n", ptr); } void operator delete(void *ptr) throw () { ::free(ptr); remove_object_block(ptr); //TRACE1("delete() called, %08x\n", ptr); } #endif // NDEBUG