mmgr.cpp: uses assert2 when something fails (so you get a pretty stack trace

mmgr.h: remove everything if !defined(USE_MMGR); move dox to end of file
debug.h: fix stupid n00b bug in assert2 (expr wasn't in "()" )

This was SVN commit r1822.
This commit is contained in:
janwas 2005-01-26 00:33:45 +00:00
parent ed27392768
commit 2d1ba66dc8
3 changed files with 126 additions and 116 deletions

View File

@ -60,7 +60,7 @@ static void lock_init() throw()
static void lock_shutdown() throw()
{
int ret = pthread_mutex_destroy(&mutex);
assert(ret == 0);
assert2(ret == 0);
lock_initialized = false;
}
@ -69,7 +69,7 @@ static void lock() throw()
if(lock_initialized)
{
int ret = pthread_mutex_lock(&mutex);
assert(ret == 0);
assert2(ret == 0);
}
}
@ -78,7 +78,7 @@ static void unlock() throw()
if(lock_initialized)
{
int ret = pthread_mutex_unlock(&mutex);
assert(ret == 0);
assert2(ret == 0);
}
}
@ -115,7 +115,7 @@ uint mmgr_set_options(uint new_options)
if(new_options != MMGR_QUERY)
{
assert(!(new_options & ~MMGR_ALL) && "unrecognized options set");
assert2(!(new_options & ~MMGR_ALL) && "unrecognized options set");
options = new_options;
}
uint ret = options;
@ -164,7 +164,7 @@ static const char* insert_commas(char* out, size_t value)
char num[NUM_SIZE];
sprintf(num, "%u", value);
const size_t num_len = strlen(num);
assert(num_len != 0); // messes up #comma calc below
assert2(num_len != 0); // messes up #comma calc below
const size_t out_len = num_len + (num_len-1)/3;
char* pos = out+out_len;
@ -259,7 +259,7 @@ static Alloc* alloc_new()
freelist = (Alloc*)calloc(256, sizeof(Alloc));
if(!freelist)
{
assert(0 && "mmgr: failed to allocate freelist (out of memory)");
assert2(0 && "mmgr: failed to allocate freelist (out of memory)");
return 0;
}
@ -268,7 +268,7 @@ static Alloc* alloc_new()
const size_t bytes = (num_reservoirs + 1) * sizeof(Alloc*);
Alloc* *temp = (Alloc* *) realloc(reservoirs, bytes);
assert(temp);
assert2(temp);
if(temp)
{
reservoirs = temp;
@ -359,7 +359,7 @@ static void allocs_add(Alloc* a)
static Alloc* allocs_find(const void* user_p)
{
if(!user_p)
assert(user_p);
assert2(user_p);
Alloc* a = hash_chain(user_p);
while(a)
@ -572,7 +572,7 @@ static void vlog(const char* fmt, va_list args)
FILE* fp = fopen(log_filename, "a");
if(!fp)
{
assert(0 && "log file open failed");
assert2(0 && "log file open failed");
return;
}
@ -696,7 +696,7 @@ void mmgr_write_report(void)
FILE* f = fopen("mem_report.txt", "w");
if(!f)
{
assert(0 && "open of memory report file failed");
assert2(0 && "open of memory report file failed");
return;
}
@ -763,7 +763,7 @@ void mmgr_write_leak_report(void)
FILE* f = fopen("mem_leaks.txt", "w");
if(!f)
{
assert(0 && "open of memory leak report file failed");
assert2(0 && "open of memory leak report file failed");
return;
}
@ -812,7 +812,7 @@ static bool alloc_is_valid(const Alloc* a)
// this allocation has been over/underrun, i.e. modified outside the
// allocation's memory range.
assert(0);
assert2(0);
log("[!] Memory over/underrun:\n");
log_alloc(a);
return false;
@ -846,13 +846,13 @@ static bool validate_all()
// enable MMGR_VALIDATE_ALL, trigger this condition again,
// and check the log for the last successful operation. the problem
// will have occurred between then and now.
assert(0);
assert2(0);
log("[!] Memory tracking hash table corrupt!\n");
}
if(params.num_invalid)
{
assert(0);
assert2(0);
log("[!] %d allocations are corrupt\n", params.num_invalid);
}
@ -933,13 +933,13 @@ void mmgr_break_on_realloc(const void* p)
Alloc* a = allocs_find(p);
if(!a)
{
assert(0 && "setting realloc breakpoint on invalid pointer");
assert2(0 && "setting realloc breakpoint on invalid pointer");
return;
}
// setting realloc breakpoint on an allocation that
// doesn't support realloc.
assert(a->type == m_alloc_malloc || a->type == m_alloc_calloc ||
assert2(a->type == m_alloc_malloc || a->type == m_alloc_calloc ||
a->type == m_alloc_realloc);
a->break_on_realloc = true;
@ -955,7 +955,7 @@ void mmgr_break_on_free(const void* p)
Alloc* a = allocs_find(p);
if(!a)
{
assert(0 && "setting free breakpoint on invalid pointer");
assert2(0 && "setting free breakpoint on invalid pointer");
return;
}
@ -1001,7 +1001,7 @@ void* alloc_dbg(size_t user_size, AllocType type, const char* file, int line, co
// you requested a breakpoint on this allocation number
++cur_alloc_count;
assert(cur_alloc_count != break_on_count);
assert2(cur_alloc_count != break_on_count);
// simulate random failures
#ifdef RANDOM_FAILURE
@ -1020,7 +1020,7 @@ void* alloc_dbg(size_t user_size, AllocType type, const char* file, int line, co
void* p = malloc(size);
if(!p)
{
assert(0);
assert2(0);
log("[!] Allocation failed (out of memory)\n");
goto fail;
}
@ -1038,7 +1038,7 @@ void* alloc_dbg(size_t user_size, AllocType type, const char* file, int line, co
store_owner(a->owner, file, line, func);
// caller's source file didn't include "mmgr.h"
assert(type != m_alloc_unknown);
assert2(type != m_alloc_unknown);
allocs_add(a);
stats_add(a);
@ -1108,16 +1108,16 @@ void free_dbg(const void* user_p, AllocType type, const char* file, int line, co
if(!a)
{
// you tried to free a pointer mmgr didn't allocate
assert(0);
assert2(0);
log("[!] mmgr_free: not allocated by this memory manager\n");
goto fail;
}
// .. overrun? (note: alloc_is_valid already asserts if invalid)
alloc_is_valid(a);
// .. the owner wasn't compiled with mmgr.h
assert(type != m_alloc_unknown);
assert2(type != m_alloc_unknown);
// .. allocator / deallocator type mismatch
assert(
assert2(
(type == m_alloc_delete && a->type == m_alloc_new ) ||
(type == m_alloc_delete_array && a->type == m_alloc_new_array) ||
(type == m_alloc_free && a->type == m_alloc_malloc ) ||
@ -1125,7 +1125,7 @@ void free_dbg(const void* user_p, AllocType type, const char* file, int line, co
(type == m_alloc_free && a->type == m_alloc_realloc )
);
// .. you requested a breakpoint when freeing this allocation
assert(!a->break_on_free);
assert2(!a->break_on_free);
// "poison" the allocation's memory, to catch use-after-free bugs.
@ -1169,7 +1169,7 @@ void* realloc_dbg(const void* user_p, size_t user_size, AllocType type, const ch
{
void* ret = 0;
assert(type == m_alloc_realloc);
assert2(type == m_alloc_realloc);
lock();
@ -1202,18 +1202,18 @@ void* realloc_dbg(const void* user_p, size_t user_size, AllocType type, const ch
if(!a)
{
// you called realloc for a pointer mmgr didn't allocate
assert(0);
assert2(0);
log("[!] realloc: wasn't previously allocated\n");
goto fail;
}
// .. the owner wasn't compiled with mmgr.h
assert(a->type != m_alloc_unknown);
assert2(a->type != m_alloc_unknown);
// .. realloc for an allocation type that doesn't support it.
assert(a->type == m_alloc_malloc || a->type == m_alloc_calloc ||
assert2(a->type == m_alloc_malloc || a->type == m_alloc_calloc ||
a->type == m_alloc_realloc);
// .. you requested a breakpoint when reallocating this allocation
// (it will continue to be triggered unless you clear a->break_on_realloc)
assert(!a->break_on_realloc);
assert2(!a->break_on_realloc);
}
// else: skip security checks; realloc(0, size) is equivalent to malloc

View File

@ -16,90 +16,22 @@
// Jan.Wassenberg@stud.uni-karlsruhe.de
// http://www.stud.uni-karlsruhe.de/~urkt/
// !!!!!!!!!! see user guide / documentation at end of file !!!!!!!!!!
// provide for completely disabling the memory manager
// (e.g. when using other debug packages)
//
// note: checking here messes up include guard detection, but we need to
// cover both the guarded part (constants+externs) and the macros.
#ifdef USE_MMGR
#ifndef MMGR_H__
#define MMGR_H__
#include "types.h"
// purpose and history
// -------------------
// our goal is to expose any memory handling bugs in the
// application as early as possible, by triggering
// breakpoints when a problem is detected.
// if all options are on, we can spot the following:
// memory leaks, double-free, allocation over/underruns,
// unused memory, and use-after-free.
//
// this code started life as Paul Nettle's memory manager (available
// at http://www.fluidstudios.com), and has been completely overhauled.
// in particular, it is now thread-safe and modularized;
// duplicated code has been eliminated.
//
//
// instructions for integrating into your project
// ----------------------------------------------
//
// 1) #include this from all project source files [that will allocate memory].
// 2) all system headers must be #include-d before this header, so that
// we don't mess with any of their local operator new/delete.
// 3) if project source/headers also use local operator new/delete, #include
// "nommgr.h" before that spot, and re-#include "mmgr.h" afterwards.
//
// 4) if using MFC:
// - set linker option /FORCE - works around conflict between our global
// operator new and that of MFC. be sure to check for other errors.
// - remove any #define new DEBUG_NEW from all source files.
//
//
// effects
// -------
//
// many bugs are caught and announced with no further changes
// required, due to integrity checks inside the allocator.
//
// at exit, three report files are generated: a listing of leaks,
// various statistics (e.g. total unused memory), and the log.
// this lists (depending on settings) all allocations, enter/exit
// indications for our functions, and failure notifications.
//
//
// digging deeper
// --------------
//
// when tracking down hard-to-find bugs, more stringent checks can be
// activated via mmgr_set_option, or by changing the initial value of
// options in mmgr.cpp. however, they slow down the app considerably
// and need not always be enabled. see option declarations below.
//
// you can also change padding_size in mmgr.cpp at compile-time to provide
// more safety vs. overruns, at the cost of wasting lots of memory per
// allocation (which must also be cleared). this is only done in
// PARANOIA builds, because overruns seldom 'skip' padding.
//
// finally, you can induce memory allocations to fail a certain percentage
// of the time - this tests your application's error handling.
// adjust the RANDOM_FAILURE #define in mmgr.cpp.
//
//
// fixing your bugs
// ----------------
//
// if this code crashes or fails an assert, it is most likely due to a bug
// in your application. consult the current Alloc for information;
// search the log for its address to determine what operation failed,
// and what piece of code owns the allocation.
//
// if the cause isn't visible (i.e. the error is reported after the fact),
// you can try activating the more stringent checks to catch the problem
// earlier. you may also call the validation routines at checkpoints
// in your code to narrow the cause down. if all else fails, break on
// the allocation number to see what's happening.
//
// good luck!
//
// optional additional checks, enabled via mmgr_set_options.
// these slow down the application; see 'digging deeper' above.
@ -205,5 +137,83 @@ extern void operator delete[](void* p, const char* file, int line, const char* f
#define realloc(p,size) mmgr_realloc_dbg(p,size, __FILE__,__LINE__,__FUNCTION__)
#define free(p) mmgr_free_dbg (p, __FILE__,__LINE__,__FUNCTION__)
// avoid macro replacement of new for local operator new
#define operator_new operator n##ew
#endif // #ifdef USE_MMGR
// purpose and history
// -------------------
// our goal is to expose any memory handling bugs in the
// application as early as possible, by triggering
// breakpoints when a problem is detected.
// if all options are on, we can spot the following:
// memory leaks, double-free, allocation over/underruns,
// unused memory, and use-after-free.
//
// this code started life as Paul Nettle's memory manager (available
// at http://www.fluidstudios.com), and has been completely overhauled.
// in particular, it is now thread-safe and modularized;
// duplicated code has been eliminated.
//
//
// instructions for integrating into your project
// ----------------------------------------------
//
// 1) #include this from all project source files [that will allocate memory].
// 2) all system headers must be #include-d before this header, so that
// we don't mess with any of their local operator new/delete.
// 3) if project source/headers also use local operator new/delete, #include
// "nommgr.h" before that spot, and re-#include "mmgr.h" afterwards.
//
// 4) if using MFC:
// - set linker option /FORCE - works around conflict between our global
// operator new and that of MFC. be sure to check for other errors.
// - remove any #define new DEBUG_NEW from all source files.
//
//
// effects
// -------
//
// many bugs are caught and announced with no further changes
// required, due to integrity checks inside the allocator.
//
// at exit, three report files are generated: a listing of leaks,
// various statistics (e.g. total unused memory), and the log.
// this lists (depending on settings) all allocations, enter/exit
// indications for our functions, and failure notifications.
//
//
// digging deeper
// --------------
//
// when tracking down hard-to-find bugs, more stringent checks can be
// activated via mmgr_set_option, or by changing the initial value of
// options in mmgr.cpp. however, they slow down the app considerably
// and need not always be enabled. see option declarations below.
//
// you can also change padding_size in mmgr.cpp at compile-time to provide
// more safety vs. overruns, at the cost of wasting lots of memory per
// allocation (which must also be cleared). this is only done in
// PARANOIA builds, because overruns seldom 'skip' padding.
//
// finally, you can induce memory allocations to fail a certain percentage
// of the time - this tests your application's error handling.
// adjust the RANDOM_FAILURE #define in mmgr.cpp.
//
//
// fixing your bugs
// ----------------
//
// if this code crashes or fails an assert, it is most likely due to a bug
// in your application. consult the current Alloc for information;
// search the log for its address to determine what operation failed,
// and what piece of code owns the allocation.
//
// if the cause isn't visible (i.e. the error is reported after the fact),
// you can try activating the more stringent checks to catch the problem
// earlier. you may also call the validation routines at checkpoints
// in your code to narrow the cause down. if all else fails, break on
// the allocation number to see what's happening.
//
// good luck!

View File

@ -56,16 +56,16 @@ extern void debug_check_heap(void);
#define assert2(expr)\
{\
static int suppress__ = 0;\
if(!suppress__ && !expr)\
if(!suppress__ && !(expr))\
switch(debug_assert_failed(__FILE__, __LINE__, #expr))\
{\
{\
case 1:\
suppress__ = 1;\
break;\
case 2:\
suppress__ = 1;\
break;\
case 2:\
debug_break();\
break;\
}\
}\
}