From: Michael D. Lowis Date: Wed, 20 Aug 2014 02:36:23 +0000 (-0400) Subject: Update mem module to count allocations and deletions for a lightweight form of leak... X-Git-Url: https://git.mdlowis.com/?a=commitdiff_plain;h=d8ae2ab2f9e5910ab8afbe1ae5cbcac6f6daa33f;p=projs%2Flibcds.git Update mem module to count allocations and deletions for a lightweight form of leak detection --- diff --git a/Rakefile b/Rakefile index cc18841..bd197ea 100644 --- a/Rakefile +++ b/Rakefile @@ -19,7 +19,7 @@ task(:posix){ is_windows = false } # Define the compiler environment Env = Rscons::Environment.new do |env| - env.build_dir('source/','build/obj/source') + env.build_dir('source','build/obj/source') env["CFLAGS"] += ['-Wall', '-Wextra', '-Werror'] env['CPPPATH'] += Dir['source/**/'] end @@ -28,7 +28,7 @@ end TestEnv = Env.clone do |env| env.build_dir('source','build/obj/test_source') env.build_dir('tests','build/obj/tests/source') - env['CFLAGS'] += ['-DLEAK_DETECTION'] + #env['CFLAGS'] += ['-DLEAK_DETECT_LEVEL=1'] env['CPPPATH'] += Dir['tests/'] end diff --git a/source/mem/mem.c b/source/mem/mem.c index 51a85f9..81b9b2d 100644 --- a/source/mem/mem.c +++ b/source/mem/mem.c @@ -1,6 +1,6 @@ #include "mem.h" #include -#ifdef LEAK_DETECTION +#ifdef LEAK_DETECT_LEVEL #include #include #endif @@ -14,22 +14,18 @@ typedef struct { intptr_t val; } box_t; -/* If LEAK_DETECTION is turned on then we need to disable the mem_allocate macro +/* If leak detection is turned on then we need to disable the mem_allocate macro * for the duration of this file. This allows us to use the original * mem_allocate function without modification in mem_allocate_ld */ -#ifdef LEAK_DETECTION +#if (LEAK_DETECT_LEVEL == 2) #undef mem_allocate +void* mem_allocate(size_t size, destructor_t p_destruct_fn); #endif -void* mem_allocate(size_t size, destructor_t p_destruct_fn) -{ - obj_t* p_obj = (obj_t*)malloc(sizeof(obj_t) + size); - p_obj->refcount = 1; - p_obj->p_finalize = p_destruct_fn; - return (void*)(p_obj+1); -} +#if (LEAK_DETECT_LEVEL > 0) +bool Handler_Registered = false; -#ifdef LEAK_DETECTION +#if (LEAK_DETECT_LEVEL == 2) typedef struct block_t { void* p_obj; const char* p_file; @@ -38,10 +34,12 @@ typedef struct block_t { } block_t; block_t* Live_Blocks = NULL; +#elif (LEAK_DETECT_LEVEL == 1) +size_t Num_Allocations = 0; +#endif -bool Handler_Registered = false; - -static void print_live_objects(void) { +static void summarize_leaks(void) { + #if (LEAK_DETECT_LEVEL == 2) bool leak_detected = false; block_t* p_curr = Live_Blocks; /* Print out all the live blocks and where they were allocated from */ @@ -59,8 +57,15 @@ static void print_live_objects(void) { } if(leak_detected) puts("Memory leak(s) detected!"); + #elif (LEAK_DETECT_LEVEL == 1) + if(Num_Allocations > 0) { + puts("Warning: Memory leak(s) detected!"); + printf("\nFor more details set the LEAK_DETECT_LEVEL build option to 2 or run the executable in valgrind.\n"); + } + #endif } +#if (LEAK_DETECT_LEVEL == 2) void* mem_allocate_ld(size_t size, destructor_t p_destruct_fn, const char* p_file, int line) { /* Allocate the object through the ordinary method */ @@ -76,21 +81,15 @@ void* mem_allocate_ld(size_t size, destructor_t p_destruct_fn, const char* p_fil * unfreed objects before the program quits */ if(!Handler_Registered) { - atexit(print_live_objects); + atexit(summarize_leaks); Handler_Registered = true; } return p_obj; } #endif +#endif -void* mem_retain(void* p_obj) -{ - obj_t* p_hdr = (((obj_t*)p_obj)-1); - p_hdr->refcount += 1; - return p_obj; -} - -#ifdef LEAK_DETECTION +#if (LEAK_DETECT_LEVEL == 2) static void deregister_block(void* p_obj) { block_t* p_prev = NULL; @@ -120,21 +119,22 @@ static void deregister_block(void* p_obj) } #endif -void mem_release(void* p_obj) +void* mem_allocate(size_t size, destructor_t p_destruct_fn) { - obj_t* p_hdr = (((obj_t*)p_obj)-1); - p_hdr->refcount -= 1; - if(p_hdr->refcount < 1) + obj_t* p_obj = (obj_t*)malloc(sizeof(obj_t) + size); + p_obj->refcount = 1; + p_obj->p_finalize = p_destruct_fn; + #if (LEAK_DETECT_LEVEL == 1) + Num_Allocations++; + /* If we haven't already, register an exit handler that will printout the + * unfreed objects before the program quits */ + if(!Handler_Registered) { - #ifdef LEAK_DETECTION - deregister_block(p_obj); - #endif - if(p_hdr->p_finalize) - { - p_hdr->p_finalize(p_obj); - } - free(p_hdr); + atexit(summarize_leaks); + Handler_Registered = true; } + #endif + return (void*)(p_obj+1); } int mem_num_references(void* p_obj) @@ -143,13 +143,28 @@ int mem_num_references(void* p_obj) return p_hdr->refcount; } -void mem_autorelease(void* p_obj) +void* mem_retain(void* p_obj) { - (void)p_obj; + obj_t* p_hdr = (((obj_t*)p_obj)-1); + p_hdr->refcount += 1; + return p_obj; } -void mem_releaseall(void) +void mem_release(void* p_obj) { + obj_t* p_hdr = (((obj_t*)p_obj)-1); + p_hdr->refcount -= 1; + if(p_hdr->refcount < 1) + { + #if (LEAK_DETECT_LEVEL == 2) + deregister_block(p_obj); + #endif + if(p_hdr->p_finalize) + { + p_hdr->p_finalize(p_obj); + } + free(p_hdr); + } } void* mem_box(intptr_t val) diff --git a/source/mem/mem.h b/source/mem/mem.h index 5b2cafd..b621e4e 100644 --- a/source/mem/mem.h +++ b/source/mem/mem.h @@ -17,12 +17,12 @@ extern "C" { /** A function pointer for object destructors */ typedef void (*destructor_t)(void* p_val); -/* If debug is disabled, disable leak detection as well */ -#ifdef NDEBUG -#undef LEAK_DETECTION +/** Unless otherwise specified, no leak detection will occur */ +#ifndef LEAK_DETECT_LEVEL +#define LEAK_DETECT_LEVEL 0 #endif -#ifndef LEAK_DETECTION +#if (LEAK_DETECT_LEVEL < 2) /** * @brief Allocates a new reference counted object of the given size which will * be destructed with the given function before it's memory is reclaimed. @@ -63,19 +63,6 @@ void* mem_retain(void* p_obj); */ void mem_release(void* p_obj); -/** - * @brief Schedules an object to be released at a later time when more convenient. - * - * @param p_obj The object to be released. - */ -void mem_autorelease(void* p_obj); - -/** - * @brief Release all objects scheduled to be released reclaiming their memory - * if necessary. - */ -void mem_releaseall(void); - /** * @brief Create a reference counted box holding the given value so that it can * be placed in a container. diff --git a/tests/test_list.c b/tests/test_list.c index 5965223..cd61a72 100644 --- a/tests/test_list.c +++ b/tests/test_list.c @@ -20,7 +20,6 @@ TEST_SUITE(List) { CHECK( NULL != list ); CHECK( NULL == list->head ); CHECK( NULL == list->tail ); - mem_release( list ); } //-------------------------------------------------------------------------