diff options
Diffstat (limited to 'intern/guardedalloc/intern')
-rw-r--r-- | intern/guardedalloc/intern/leak_detector.cc | 61 | ||||
-rw-r--r-- | intern/guardedalloc/intern/mallocn_guarded_impl.c | 165 | ||||
-rw-r--r-- | intern/guardedalloc/intern/mallocn_inline.h | 8 | ||||
-rw-r--r-- | intern/guardedalloc/intern/mallocn_intern.h | 11 | ||||
-rw-r--r-- | intern/guardedalloc/intern/mallocn_lockfree_impl.c | 4 |
5 files changed, 194 insertions, 55 deletions
diff --git a/intern/guardedalloc/intern/leak_detector.cc b/intern/guardedalloc/intern/leak_detector.cc new file mode 100644 index 00000000000..d7b6f749742 --- /dev/null +++ b/intern/guardedalloc/intern/leak_detector.cc @@ -0,0 +1,61 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/** \file + * \ingroup MEM + */ + +#include "MEM_guardedalloc.h" +#include "mallocn_intern.h" + +bool leak_detector_has_run = false; +char free_after_leak_detection_message[] = + "Freeing memory after the leak detector has run. This can happen when using " + "static variables in C++ that are defined outside of functions. To fix this " + "error, use the 'construct on first use' idiom."; + +namespace { +class MemLeakPrinter { + public: + ~MemLeakPrinter() + { + leak_detector_has_run = true; + const uint leaked_blocks = MEM_get_memory_blocks_in_use(); + if (leaked_blocks == 0) { + return; + } + const size_t mem_in_use = MEM_get_memory_in_use(); + printf("Error: Not freed memory blocks: %u, total unfreed memory %f MB\n", + leaked_blocks, + (double)mem_in_use / 1024 / 1024); + MEM_printmemlist(); + } +}; +} // namespace + +void MEM_init_memleak_detection(void) +{ + /** + * This variable is constructed when this function is first called. This should happen as soon as + * possible when the program starts. + * + * It is destructed when the program exits. During destruction, it will print information about + * leaked memory blocks. Static variables are destructed in reversed order of their + * construction. Therefore, all static variables that own memory have to be constructed after + * this function has been called. + */ + static MemLeakPrinter printer; +} diff --git a/intern/guardedalloc/intern/mallocn_guarded_impl.c b/intern/guardedalloc/intern/mallocn_guarded_impl.c index 20dcbed7235..2c207935e43 100644 --- a/intern/guardedalloc/intern/mallocn_guarded_impl.c +++ b/intern/guardedalloc/intern/mallocn_guarded_impl.c @@ -198,10 +198,12 @@ print_error(const char *str, ...) va_end(ap); buf[sizeof(buf) - 1] = '\0'; - if (error_callback) + if (error_callback) { error_callback(buf); - else + } + else { fputs(buf, stderr); + } } static pthread_mutex_t thread_lock = PTHREAD_MUTEX_INITIALIZER; @@ -261,13 +263,16 @@ void *MEM_guarded_dupallocN(const void *vmemh) memh--; #ifndef DEBUG_MEMDUPLINAME - if (LIKELY(memh->alignment == 0)) + if (LIKELY(memh->alignment == 0)) { newp = MEM_guarded_mallocN(memh->len, "dupli_alloc"); - else + } + else { newp = MEM_guarded_mallocN_aligned(memh->len, (size_t)memh->alignment, "dupli_alloc"); + } - if (newp == NULL) + if (newp == NULL) { return NULL; + } #else { MemHead *nmemh; @@ -450,8 +455,9 @@ void *MEM_guarded_mallocN(size_t len, const char *str) if (LIKELY(memh)) { make_memhead_header(memh, len, str); - if (UNLIKELY(malloc_debug_memset && len)) + if (UNLIKELY(malloc_debug_memset && len)) { memset(memh + 1, 255, len); + } #ifdef DEBUG_MEMCOUNTER if (_mallocn_count == DEBUG_MEMCOUNTER_ERROR_VAL) @@ -522,8 +528,9 @@ void *MEM_guarded_mallocN_aligned(size_t len, size_t alignment, const char *str) make_memhead_header(memh, len, str); memh->alignment = (short)alignment; - if (UNLIKELY(malloc_debug_memset && len)) + if (UNLIKELY(malloc_debug_memset && len)) { memset(memh + 1, 255, len); + } #ifdef DEBUG_MEMCOUNTER if (_mallocn_count == DEBUG_MEMCOUNTER_ERROR_VAL) @@ -601,12 +608,15 @@ static int compare_len(const void *p1, const void *p2) const MemPrintBlock *pb1 = (const MemPrintBlock *)p1; const MemPrintBlock *pb2 = (const MemPrintBlock *)p2; - if (pb1->len < pb2->len) + if (pb1->len < pb2->len) { return 1; - else if (pb1->len == pb2->len) + } + else if (pb1->len == pb2->len) { return 0; - else + } + else { return -1; + } } void MEM_guarded_printmemlist_stats(void) @@ -636,8 +646,9 @@ void MEM_guarded_printmemlist_stats(void) totpb = 0; membl = membase->first; - if (membl) + if (membl) { membl = MEMNEXT(membl); + } while (membl && pb) { pb->name = membl->name; @@ -654,10 +665,12 @@ void MEM_guarded_printmemlist_stats(void) } #endif - if (membl->next) + if (membl->next) { membl = MEMNEXT(membl->next); - else + } + else { break; + } } /* sort by name and add together blocks with the same name */ @@ -737,8 +750,9 @@ static void MEM_guarded_printmemlist_internal(int pydict) mem_lock_thread(); membl = membase->first; - if (membl) + if (membl) { membl = MEMNEXT(membl); + } if (pydict) { print_error("# membase_debug.py\n"); @@ -771,10 +785,12 @@ static void MEM_guarded_printmemlist_internal(int pydict) print_memhead_backtrace(membl); #endif } - if (membl->next) + if (membl->next) { membl = MEMNEXT(membl->next); - else + } + else { break; + } } if (pydict) { print_error("]\n\n"); @@ -791,15 +807,18 @@ void MEM_guarded_callbackmemlist(void (*func)(void *)) mem_lock_thread(); membl = membase->first; - if (membl) + if (membl) { membl = MEMNEXT(membl); + } while (membl) { func(membl + 1); - if (membl->next) + if (membl->next) { membl = MEMNEXT(membl->next); - else + } + else { break; + } } mem_unlock_thread(); @@ -879,6 +898,10 @@ void MEM_guarded_freeN(void *vmemh) memt = (MemTail *)(((char *)memh) + sizeof(MemHead) + memh->len); if (memt->tag3 == MEMTAG3) { + if (leak_detector_has_run) { + MemorY_ErroR(memh->name, free_after_leak_detection_message); + } + memh->tag1 = MEMFREE; memh->tag2 = MEMFREE; memt->tag3 = MEMFREE; @@ -890,24 +913,25 @@ void MEM_guarded_freeN(void *vmemh) MemorY_ErroR(memh->name, "end corrupt"); name = check_memlist(memh); if (name != NULL) { - if (name != memh->name) + if (name != memh->name) { MemorY_ErroR(name, "is also corrupt"); + } } } else { mem_lock_thread(); name = check_memlist(memh); mem_unlock_thread(); - if (name == NULL) + if (name == NULL) { MemorY_ErroR("free", "pointer not in memlist"); - else + } + else { MemorY_ErroR(name, "error in header"); + } } totblock--; /* here a DUMP should happen */ - - return; } /* --------------------------------------------------------------------- */ @@ -930,10 +954,12 @@ static void addtail(volatile localListBase *listbase, void *vlink) link->next = NULL; link->prev = listbase->last; - if (listbase->last) + if (listbase->last) { ((struct localLink *)listbase->last)->next = link; - if (listbase->first == NULL) + } + if (listbase->first == NULL) { listbase->first = link; + } listbase->last = link; } @@ -950,15 +976,19 @@ static void remlink(volatile localListBase *listbase, void *vlink) return; #endif - if (link->next) + if (link->next) { link->next->prev = link->prev; - if (link->prev) + } + if (link->prev) { link->prev->next = link->next; + } - if (listbase->last == link) + if (listbase->last == link) { listbase->last = link->prev; - if (listbase->first == link) + } + if (listbase->first == link) { listbase->first = link->next; + } } static void rem_memblock(MemHead *memh) @@ -966,10 +996,12 @@ static void rem_memblock(MemHead *memh) mem_lock_thread(); remlink(membase, &memh->next); if (memh->prev) { - if (memh->next) + if (memh->next) { MEMNEXT(memh->prev)->nextname = MEMNEXT(memh->next)->name; - else + } + else { MEMNEXT(memh->prev)->nextname = NULL; + } } mem_unlock_thread(); @@ -981,8 +1013,9 @@ static void rem_memblock(MemHead *memh) free((char *)memh->name); #endif - if (UNLIKELY(malloc_debug_memset && memh->len)) + if (UNLIKELY(malloc_debug_memset && memh->len)) { memset(memh + 1, 255, memh->len); + } if (LIKELY(memh->alignment == 0)) { free(memh); } @@ -1006,78 +1039,100 @@ static const char *check_memlist(MemHead *memh) const char *name; forw = membase->first; - if (forw) + if (forw) { forw = MEMNEXT(forw); + } forwok = NULL; while (forw) { - if (forw->tag1 != MEMTAG1 || forw->tag2 != MEMTAG2) + if (forw->tag1 != MEMTAG1 || forw->tag2 != MEMTAG2) { break; + } forwok = forw; - if (forw->next) + if (forw->next) { forw = MEMNEXT(forw->next); - else + } + else { forw = NULL; + } } back = (MemHead *)membase->last; - if (back) + if (back) { back = MEMNEXT(back); + } backok = NULL; while (back) { - if (back->tag1 != MEMTAG1 || back->tag2 != MEMTAG2) + if (back->tag1 != MEMTAG1 || back->tag2 != MEMTAG2) { break; + } backok = back; - if (back->prev) + if (back->prev) { back = MEMNEXT(back->prev); - else + } + else { back = NULL; + } } - if (forw != back) + if (forw != back) { return ("MORE THAN 1 MEMORYBLOCK CORRUPT"); + } if (forw == NULL && back == NULL) { /* no wrong headers found then but in search of memblock */ forw = membase->first; - if (forw) + if (forw) { forw = MEMNEXT(forw); + } forwok = NULL; while (forw) { - if (forw == memh) + if (forw == memh) { break; - if (forw->tag1 != MEMTAG1 || forw->tag2 != MEMTAG2) + } + if (forw->tag1 != MEMTAG1 || forw->tag2 != MEMTAG2) { break; + } forwok = forw; - if (forw->next) + if (forw->next) { forw = MEMNEXT(forw->next); - else + } + else { forw = NULL; + } } - if (forw == NULL) + if (forw == NULL) { return NULL; + } back = (MemHead *)membase->last; - if (back) + if (back) { back = MEMNEXT(back); + } backok = NULL; while (back) { - if (back == memh) + if (back == memh) { break; - if (back->tag1 != MEMTAG1 || back->tag2 != MEMTAG2) + } + if (back->tag1 != MEMTAG1 || back->tag2 != MEMTAG2) { break; + } backok = back; - if (back->prev) + if (back->prev) { back = MEMNEXT(back->prev); - else + } + else { back = NULL; + } } } - if (forwok) + if (forwok) { name = forwok->nextname; - else + } + else { name = "No name found"; + } if (forw == memh) { /* to be sure but this block is removed from the list */ diff --git a/intern/guardedalloc/intern/mallocn_inline.h b/intern/guardedalloc/intern/mallocn_inline.h index f8bb7861fc9..4e73eb9bad6 100644 --- a/intern/guardedalloc/intern/mallocn_inline.h +++ b/intern/guardedalloc/intern/mallocn_inline.h @@ -33,6 +33,10 @@ #ifndef __MALLOCN_INLINE_H__ #define __MALLOCN_INLINE_H__ +#ifdef __cplusplus +extern "C" { +#endif + MEM_INLINE bool MEM_size_safe_multiply(size_t a, size_t b, size_t *result) { /* A size_t with its high-half bits all set to 1. */ @@ -52,4 +56,8 @@ MEM_INLINE bool MEM_size_safe_multiply(size_t a, size_t b, size_t *result) return ((high_bits & (a | b)) == 0 || (*result / b == a)); } +#ifdef __cplusplus +} +#endif + #endif /* __MALLOCN_INLINE_H__ */ diff --git a/intern/guardedalloc/intern/mallocn_intern.h b/intern/guardedalloc/intern/mallocn_intern.h index ef8845a66b3..8fc3e432157 100644 --- a/intern/guardedalloc/intern/mallocn_intern.h +++ b/intern/guardedalloc/intern/mallocn_intern.h @@ -100,11 +100,18 @@ size_t malloc_usable_size(void *ptr); #include "mallocn_inline.h" +#ifdef __cplusplus +extern "C" { +#endif + #define ALIGNED_MALLOC_MINIMUM_ALIGNMENT sizeof(void *) void *aligned_malloc(size_t size, size_t alignment); void aligned_free(void *ptr); +extern bool leak_detector_has_run; +extern char free_after_leak_detection_message[]; + /* Prototypes for counted allocator functions */ size_t MEM_lockfree_allocN_len(const void *vmemh) ATTR_WARN_UNUSED_RESULT; void MEM_lockfree_freeN(void *vmemh); @@ -191,4 +198,8 @@ size_t MEM_guarded_get_peak_memory(void) ATTR_WARN_UNUSED_RESULT; const char *MEM_guarded_name_ptr(void *vmemh); #endif +#ifdef __cplusplus +} +#endif + #endif /* __MALLOCN_INTERN_H__ */ diff --git a/intern/guardedalloc/intern/mallocn_lockfree_impl.c b/intern/guardedalloc/intern/mallocn_lockfree_impl.c index 205cc688d72..b71e2c963eb 100644 --- a/intern/guardedalloc/intern/mallocn_lockfree_impl.c +++ b/intern/guardedalloc/intern/mallocn_lockfree_impl.c @@ -101,6 +101,10 @@ size_t MEM_lockfree_allocN_len(const void *vmemh) void MEM_lockfree_freeN(void *vmemh) { + if (leak_detector_has_run) { + print_error("%s\n", free_after_leak_detection_message); + } + MemHead *memh = MEMHEAD_FROM_PTR(vmemh); size_t len = MEM_lockfree_allocN_len(vmemh); |