From 8810c00f837a585d4dc6079640abc3251f0e70ca Mon Sep 17 00:00:00 2001 From: Camille Oudot Date: Tue, 19 May 2015 19:58:57 +0200 Subject: [PATCH] mem: TLSF memory summary and sums implementation TLSF now has a debug mode, that will dump every memory block when xxx_status() is called, and a summary of still allocated blocks when xxx_sums() is called. configure build with make MEMDBG=1 MEMMNG=2 cfg to enable maximum debuging informations (source location of mallocs and frees) --- Makefile.defs | 3 + mem/mem.h | 21 +++- mem/q_malloc.c | 2 +- mem/q_malloc.h | 2 +- mem/shm_mem.h | 12 ++- mem/tlsf.c | 273 ++++++++++++++++++++++++++++++++++++------------- mem/tlsf.h | 12 ++- 7 files changed, 248 insertions(+), 77 deletions(-) diff --git a/Makefile.defs b/Makefile.defs index 1ec5322b198..f97708920b4 100644 --- a/Makefile.defs +++ b/Makefile.defs @@ -668,6 +668,9 @@ endif else ifeq ($(MEMMNG), 2) # use tlsf malloc C_DEFS+= -DTLSF_MALLOC=1 +ifeq ($(MEMDBG), 1) + C_DEFS+= -DDBG_TLSF_MALLOC +endif else # use f_malloc C_DEFS+= -DF_MALLOC diff --git a/mem/mem.h b/mem/mem.h index 9db8d93572f..432f29a6793 100644 --- a/mem/mem.h +++ b/mem/mem.h @@ -49,6 +49,14 @@ #elif defined(DBG_QM_MALLOC) #define DBG_F_MALLOC #endif +#elif defined TLSF_MALLOC + #ifdef DBG_TLSF_MALLOC + #ifndef DBG_QM_MALLOC + #define DBG_QM_MALLOC + #endif + #elif defined(DBG_QM_MALLOC) + #define DBG_TLSF_MALLOC + #endif #endif #ifdef PKG_MALLOC @@ -78,6 +86,13 @@ _SRC_FUNCTION_, _SRC_LINE_) # define pkg_realloc(p, s) fm_realloc(mem_block, (p), (s), \ _SRC_LOC_, _SRC_FUNCTION_, _SRC_LINE_) +# elif defined TLSF_MALLOC +# define pkg_malloc(s) tlsf_malloc(mem_block, (s), _SRC_LOC_, \ + _SRC_FUNCTION_, _SRC_LINE_) +# define pkg_free(p) tlsf_free(mem_block, (p), _SRC_LOC_, \ + _SRC_FUNCTION_, _SRC_LINE_) +# define pkg_realloc(p, s) tlsf_realloc(mem_block, (p), (s), \ + _SRC_LOC_, _SRC_FUNCTION_, _SRC_LINE_) # else # define pkg_malloc(s) qm_malloc(mem_block, (s),_SRC_LOC_, \ _SRC_FUNCTION_, _SRC_LINE_) @@ -116,10 +131,10 @@ # define pkg_available() 0 # define pkg_sums() 0 # elif defined TLSF_MALLOC -# define pkg_status() ((void) 0) +# define pkg_status() tlsf_status(mem_block) # define pkg_info(mi) tlsf_meminfo(mem_block, (mi)) -# define pkg_available() ((void) 0) -# define pkg_sums() ((void) 0) +# define pkg_available() tlsf_available(mem_block) +# define pkg_sums() tlsf_sums(mem_block) # else # define pkg_status() qm_status(mem_block) # define pkg_info(mi) qm_info(mem_block, mi) diff --git a/mem/q_malloc.c b/mem/q_malloc.c index 070a2282fea..11ccd37063d 100644 --- a/mem/q_malloc.c +++ b/mem/q_malloc.c @@ -23,7 +23,7 @@ */ -#if !defined(q_malloc) && !(defined F_MALLOC) +#if !defined(q_malloc) && !(defined F_MALLOC) && !defined(TLSF_MALLOC) #define q_malloc #include diff --git a/mem/q_malloc.h b/mem/q_malloc.h index 0e9a70656db..e02853d4d9c 100644 --- a/mem/q_malloc.h +++ b/mem/q_malloc.h @@ -22,7 +22,7 @@ * \ingroup mem */ -#if !defined(q_malloc_h) && !defined(F_MALLOC) +#if !defined(q_malloc_h) && !defined(F_MALLOC) && !defined(TLSF_MALLOC) #define q_malloc_h #include "meminfo.h" diff --git a/mem/shm_mem.h b/mem/shm_mem.h index a240cd490db..d31ce6b9e7c 100644 --- a/mem/shm_mem.h +++ b/mem/shm_mem.h @@ -57,6 +57,14 @@ #elif defined(DBG_QM_MALLOC) #define DBG_F_MALLOC #endif +#elif defined TLSF_MALLOC + #ifdef DBG_TLSF_MALLOC + #ifndef DBG_QM_MALLOC + #define DBG_QM_MALLOC + #endif + #elif defined(DBG_QM_MALLOC) + #define DBG_TLSF_MALLOC + #endif #endif @@ -149,9 +157,9 @@ # define MY_MALLOC tlsf_malloc # define MY_FREE tlsf_free # define MY_REALLOC tlsf_realloc -# define MY_STATUS(...) ((void) 0) +# define MY_STATUS tlsf_status # define MY_MEMINFO tlsf_meminfo -# define MY_SUMS(...) ((void) 0) +# define MY_SUMS tlsf_sums # define shm_malloc_init(mem, bytes, type) tlsf_create_with_pool((void*) mem, bytes) # define shm_malloc_destroy(b) do{}while(0) # define shm_available() tlsf_available(shm_block) diff --git a/mem/tlsf.c b/mem/tlsf.c index a800dd1ef06..d4feb34e086 100644 --- a/mem/tlsf.c +++ b/mem/tlsf.c @@ -7,6 +7,9 @@ #include "tlsf.h" #include "tlsfbits.h" +#include "src_loc.h" +#include "../dprint.h" +#include "../cfg/cfg.h" /* ** Constants. @@ -96,7 +99,13 @@ tlsf_static_assert(ALIGN_SIZE == SMALL_BLOCK_SIZE / SL_INDEX_COUNT); /* ** Data structures and associated constants. */ - +#ifdef DBG_TLSF_MALLOC +typedef struct { + const char* file; + const char* func; + unsigned long line; +} alloc_info_t; +#endif /* ** Block header structure. ** @@ -112,6 +121,10 @@ typedef struct block_header_t /* Points to the previous physical block. */ struct block_header_t* prev_phys_block; +#ifdef DBG_TLSF_MALLOC + alloc_info_t alloc_info; +#endif + /* The size of this block, excluding the block header. */ size_t size; @@ -133,7 +146,11 @@ static const size_t block_header_prev_free_bit = 1 << 1; ** The size of the block header exposed to used blocks is the size field. ** The prev_phys_block field is stored *inside* the previous free block. */ +#ifdef DBG_TLSF_MALLOC +static const size_t block_header_overhead = sizeof(size_t) + sizeof(alloc_info_t); +#else static const size_t block_header_overhead = sizeof(size_t); +#endif /* User data starts directly after the size field in a used block. */ static const size_t block_start_offset = @@ -144,7 +161,7 @@ static const size_t block_start_offset = ** the prev_phys_block field, and no larger than the number of addressable ** bits for FL_INDEX. */ -static const size_t block_size_min = +static const size_t block_size_min = sizeof(block_header_t) - sizeof(block_header_t*); static const size_t block_size_max = tlsf_cast(size_t, 1) << FL_INDEX_MAX; @@ -255,7 +272,7 @@ static block_header_t* block_prev(const block_header_t* block) static block_header_t* block_next(const block_header_t* block) { block_header_t* next = offset_to_block(block_to_ptr(block), - block_size(block) - block_header_overhead); + block_size(block) - sizeof(block_header_t*)); tlsf_assert(!block_is_last(block)); return next; } @@ -467,7 +484,7 @@ static block_header_t* block_split(block_header_t* block, size_t size) { /* Calculate the amount of space left in the remaining block. */ block_header_t* remaining = - offset_to_block(block_to_ptr(block), size - block_header_overhead); + offset_to_block(block_to_ptr(block), size - sizeof(block_header_t*)); const size_t remain_size = block_size(block) - (size + block_header_overhead); @@ -476,7 +493,7 @@ static block_header_t* block_split(block_header_t* block, size_t size) tlsf_assert(block_size(block) == remain_size + size + block_header_overhead); block_set_size(remaining, remain_size); - tlsf_assert(block_size(remaining) >= block_size_min && "block split with invalid size"); + tlsf_assert(block_size(remaining) >= 0 && "block split with invalid size"); block_set_size(block, size); block_mark_as_free(remaining); @@ -534,6 +551,11 @@ static void block_trim_free(control_t* control, block_header_t* block, size_t si block_header_t* remaining_block = block_split(block, size); block_link_next(block); block_set_prev_free(remaining_block); +#ifdef DBG_TLSF_MALLOC + remaining_block->alloc_info.file = _SRC_LOC_; + remaining_block->alloc_info.func = _SRC_FUNCTION_; + remaining_block->alloc_info.line = _SRC_LINE_; +#endif block_insert(control, remaining_block); } } @@ -549,6 +571,11 @@ static void block_trim_used(control_t* control, block_header_t* block, size_t si block_set_prev_used(remaining_block); remaining_block = block_merge_next(control, remaining_block); +#ifdef DBG_TLSF_MALLOC + remaining_block->alloc_info.file = _SRC_LOC_; + remaining_block->alloc_info.func = _SRC_FUNCTION_; + remaining_block->alloc_info.line = _SRC_LINE_; +#endif block_insert(control, remaining_block); } } @@ -588,8 +615,12 @@ static block_header_t* block_locate_free(control_t* control, size_t size) return block; } - +#ifdef DBG_TLSF_MALLOC +static void* block_prepare_used(control_t* control, block_header_t* block, size_t size, + const char *file, const char *function, unsigned long line) +#else static void* block_prepare_used(control_t* control, block_header_t* block, size_t size) +#endif { void* p = 0; if (block) @@ -600,6 +631,11 @@ static void* block_prepare_used(control_t* control, block_header_t* block, size_ #ifdef TLSF_STATS TLSF_INCREASE_REAL_USED(control, block->size + (p - (void *)block)); control->allocated += block->size; +#endif +#ifdef DBG_TLSF_MALLOC + block->alloc_info.file = file; + block->alloc_info.func = function; + block->alloc_info.line = line; #endif } return p; @@ -715,8 +751,7 @@ static void default_walker(void* ptr, size_t size, int used, void* user) void tlsf_walk_pool(pool_t pool, tlsf_walker walker, void* user) { tlsf_walker pool_walker = walker ? walker : default_walker; - block_header_t* block = - offset_to_block(pool, -(int)block_header_overhead); + block_header_t* block = pool + tlsf_size() - sizeof(block_header_t*); while (block && !block_is_last(block)) { @@ -806,11 +841,11 @@ pool_t tlsf_add_pool(tlsf_t tlsf, void* mem, size_t bytes) if (pool_bytes < block_size_min || pool_bytes > block_size_max) { #if defined (TLSF_64BIT) - printf("tlsf_add_pool: Memory size must be between 0x%x and 0x%x00 bytes.\n", + printf("tlsf_add_pool: Memory size must be between 0x%x and 0x%x00 bytes.\n", (unsigned int)(pool_overhead + block_size_min), (unsigned int)((pool_overhead + block_size_max) / 256)); #else - printf("tlsf_add_pool: Memory size must be between %u and %u bytes.\n", + printf("tlsf_add_pool: Memory size must be between %u and %u bytes.\n", (unsigned int)(pool_overhead + block_size_min), (unsigned int)(pool_overhead + block_size_max)); #endif @@ -822,13 +857,18 @@ pool_t tlsf_add_pool(tlsf_t tlsf, void* mem, size_t bytes) ** so that the prev_phys_block field falls outside of the pool - ** it will never be used. */ - block = offset_to_block(mem, -(tlsfptr_t)block_header_overhead); + block = mem - sizeof (size_t);/*offset_to_block(mem, -(tlsfptr_t)block_header_overhead);*/ block_set_size(block, pool_bytes); block_set_free(block); block_set_prev_used(block); block_insert(tlsf_cast(control_t*, tlsf), block); #ifdef TLSF_STATS tlsf_cast(control_t*, tlsf)->total_size += block->size; +#endif +#ifdef DBG_TLSF_MALLOC + block->alloc_info.file = _SRC_LOC_; + block->alloc_info.func = _SRC_FUNCTION_; + block->alloc_info.line = _SRC_LINE_; #endif /* Split the block to create a zero-size sentinel block. */ next = block_link_next(block); @@ -878,7 +918,7 @@ int test_ffs_fls() #if defined (TLSF_64BIT) rv += (tlsf_fls_sizet(0x80000000) == 31) ? 0 : 0x100; rv += (tlsf_fls_sizet(0x100000000) == 32) ? 0 : 0x200; - rv += (tlsf_fls_sizet(0xffffffffffffffff) == 63) ? 0 : 0x400; + rv += (tlsf_fls_sizet(0xffffffffffffffff) == 63) ? 0 : 0x400; #endif if (rv) @@ -935,73 +975,29 @@ pool_t tlsf_get_pool(tlsf_t tlsf) return tlsf_cast(pool_t, (char*)tlsf + tlsf_size()); } +#ifdef DBG_TLSF_MALLOC +void* tlsf_malloc(tlsf_t tlsf, size_t size, + const char *file, const char *function, unsigned long line) +#else void* tlsf_malloc(tlsf_t tlsf, size_t size) +#endif { control_t* control = tlsf_cast(control_t*, tlsf); const size_t adjust = adjust_request_size(size, ALIGN_SIZE); block_header_t* block = block_locate_free(control, adjust); +#ifdef DBG_TLSF_MALLOC + return block_prepare_used(control, block, adjust, file, function, line); +#else return block_prepare_used(control, block, adjust); -} - -void* tlsf_memalign(tlsf_t tlsf, size_t align, size_t size) -{ - control_t* control = tlsf_cast(control_t*, tlsf); - const size_t adjust = adjust_request_size(size, ALIGN_SIZE); - - /* - ** We must allocate an additional minimum block size bytes so that if - ** our free block will leave an alignment gap which is smaller, we can - ** trim a leading free block and release it back to the pool. We must - ** do this because the previous physical block is in use, therefore - ** the prev_phys_block field is not valid, and we can't simply adjust - ** the size of that block. - */ - const size_t gap_minimum = sizeof(block_header_t); - const size_t size_with_gap = adjust_request_size(adjust + align + gap_minimum, align); - - /* If alignment is less than or equals base alignment, we're done. */ - const size_t aligned_size = (align <= ALIGN_SIZE) ? adjust : size_with_gap; - - block_header_t* block = block_locate_free(control, aligned_size); - - /* This can't be a static assert. */ - tlsf_assert(sizeof(block_header_t) == block_size_min + block_header_overhead); - - if (block) - { - void* ptr = block_to_ptr(block); - void* aligned = align_ptr(ptr, align); - size_t gap = tlsf_cast(size_t, - tlsf_cast(tlsfptr_t, aligned) - tlsf_cast(tlsfptr_t, ptr)); - - /* If gap size is too small, offset to next aligned boundary. */ - if (gap && gap < gap_minimum) - { - const size_t gap_remain = gap_minimum - gap; - const size_t offset = tlsf_max(gap_remain, align); - const void* next_aligned = tlsf_cast(void*, - tlsf_cast(tlsfptr_t, aligned) + offset); - - aligned = align_ptr(next_aligned, align); - gap = tlsf_cast(size_t, - tlsf_cast(tlsfptr_t, aligned) - tlsf_cast(tlsfptr_t, ptr)); - } - - if (gap) - { - tlsf_assert(gap >= gap_minimum && "gap size too small"); - block = block_trim_free_leading(control, block, gap); - } - } - -#ifdef TLSF_STATS - /* TODO */ #endif - - return block_prepare_used(control, block, adjust); } +#ifdef DBG_TLSF_MALLOC +void tlsf_free(tlsf_t tlsf, void* ptr, + const char *file, const char *function, unsigned long line) +#else void tlsf_free(tlsf_t tlsf, void* ptr) +#endif { /* Don't attempt to free a NULL pointer. */ if (ptr) @@ -1012,6 +1008,11 @@ void tlsf_free(tlsf_t tlsf, void* ptr) #if defined TLSF_STATS control->allocated -= block->size; control->real_used -= (block->size + (ptr - (void *)block)); +#endif +#ifdef DBG_TLSF_MALLOC + block->alloc_info.file = file; + block->alloc_info.func = function; + block->alloc_info.line = line; #endif block_mark_as_free(block); block = block_merge_prev(control, block); @@ -1033,7 +1034,12 @@ void tlsf_free(tlsf_t tlsf, void* ptr) ** - an extended buffer size will leave the newly-allocated area with ** contents undefined */ +#ifdef DBG_TLSF_MALLOC +void* tlsf_realloc(tlsf_t tlsf, void* ptr, size_t size, + const char *file, const char *function, unsigned long line) +#else void* tlsf_realloc(tlsf_t tlsf, void* ptr, size_t size) +#endif { control_t* control = tlsf_cast(control_t*, tlsf); void* p = 0; @@ -1041,12 +1047,20 @@ void* tlsf_realloc(tlsf_t tlsf, void* ptr, size_t size) /* Zero-size requests are treated as free. */ if (ptr && size == 0) { +#ifdef DBG_TLSF_MALLOC + tlsf_free(tlsf, ptr, file, function, line); +#else tlsf_free(tlsf, ptr); +#endif } /* Requests with NULL pointers are treated as malloc. */ else if (!ptr) { +#ifdef DBG_TLSF_MALLOC + p = tlsf_malloc(tlsf, size, file, function, line); +#else p = tlsf_malloc(tlsf, size); +#endif } else { @@ -1065,12 +1079,20 @@ void* tlsf_realloc(tlsf_t tlsf, void* ptr, size_t size) */ if (adjust > cursize && (!block_is_free(next) || adjust > combined)) { +#ifdef DBG_TLSF_MALLOC + p = tlsf_malloc(tlsf, size, file, function, line); +#else p = tlsf_malloc(tlsf, size); +#endif if (p) { const size_t minsize = tlsf_min(cursize, size); memcpy(p, ptr, minsize); +#ifdef DBG_TLSF_MALLOC + tlsf_free(tlsf, ptr, file, function, line); +#else tlsf_free(tlsf, ptr); +#endif } } else @@ -1118,5 +1140,118 @@ size_t tlsf_available(tlsf_t pool) control_t* control = tlsf_cast(control_t*, pool); return control->total_size - control->real_used; } + +static void tlsf_status_cb(void* ptr, size_t size, int used, void* user) +{ +#ifdef DBG_TLSF_MALLOC + block_header_t* block = block_from_ptr(ptr); +#endif + +#ifdef DBG_TLSF_MALLOC + LOG_(DEFAULT_FACILITY, *(int*)user, "tlsf_status: ", "(%p): used=%s size=%zu was %s from %s: %s(%ld)\n", + ptr, used ? "true" : "false", size, used ? "alloc'd" : "free'd", block->alloc_info.file, block->alloc_info.func, block->alloc_info.line); +#else + LOG_(DEFAULT_FACILITY, *(int*)user, "tlsf_status: ", "(%p): used=%s size=%zu\n", + ptr, used ? "true" : "false", size); +#endif + + +} + +void tlsf_status(tlsf_t pool) +{ + int memlog; + control_t* control = tlsf_cast(control_t*, pool); + + memlog=cfg_get(core, core_cfg, memlog); + LOG_(DEFAULT_FACILITY, memlog, "tlsf_status: ", "status of pool (%p):\n", pool); +#ifdef TLSF_STATS + LOG_(DEFAULT_FACILITY, memlog, "tlsf_status: ", "heap size= %zu\n", + control->total_size); + LOG_(DEFAULT_FACILITY, memlog, "tlsf_status: ", + "used= %zu, used+overhead=%zu, free=%zu, fragments=%zu\n", + control->allocated, control->real_used, control->total_size - control->real_used, control->fragments); + LOG_(DEFAULT_FACILITY, memlog, "tlsf_status: ", + "max used (+overhead)=%zu, max fragments=%zu\n", control->max_used, control->max_fragments); +#endif + LOG_(DEFAULT_FACILITY, memlog, "tlsf_status: ", + "dumping all fragments:\n"); + tlsf_walk_pool(pool, tlsf_status_cb, (void *) &memlog); +} + +#ifdef DBG_TLSF_MALLOC +typedef struct _mem_counter{ + const char *file; + const char *func; + unsigned long line; + + unsigned long size; + int count; + + struct _mem_counter *next; +} mem_counter; + +static mem_counter* get_mem_counter(mem_counter **root, block_header_t* f) +{ + mem_counter *x; + if (!*root) goto make_new; + for(x=*root;x;x=x->next) + if (x->file == f->alloc_info.file && x->func == f->alloc_info.func && x->line == f->alloc_info.line) + return x; +make_new: + x = malloc(sizeof(mem_counter)); + x->file = f->alloc_info.file; + x->func = f->alloc_info.func; + x->line = f->alloc_info.line; + x->count = 0; + x->size = 0; + x->next = *root; + *root = x; + return x; +} + + +void tlsf_sums(tlsf_t pool) +{ + int memlog; + block_header_t* block = pool + tlsf_size() - sizeof(block_header_t*); + mem_counter *root = NULL, *x; + + memlog=cfg_get(core, core_cfg, memlog); + + LOG_(DEFAULT_FACILITY, memlog, "tlsf_sums: ", + "pool (%p) summarizing all alloc'ed. fragments:\n", pool); + + + while (block && !block_is_last(block)) + { + if(!block_is_free(block)) { + x = get_mem_counter(&root, block); + x->count++; + x->size+=block_size(block); + } + + block = block_next(block); + } + + x = root; + while(x){ + LOG_(DEFAULT_FACILITY, memlog, "tlsf_sums: ", + " count=%6d size=%10lu bytes from %s: %s(%ld)\n", + x->count,x->size, + x->file, x->func, x->line + ); + root = x->next; + free(x); + x = root; + } + LOG_(DEFAULT_FACILITY, memlog, "tlsf_sums: ", + "-----------------------------\n"); +} +#else +void tlsf_sums(tlsf_t pool) +{} +#endif /* defined DBG_TLSF_MALLOC */ + #endif diff --git a/mem/tlsf.h b/mem/tlsf.h index 8826807f996..bcf41909011 100644 --- a/mem/tlsf.h +++ b/mem/tlsf.h @@ -40,10 +40,18 @@ pool_t tlsf_add_pool(tlsf_t tlsf, void* mem, size_t bytes); void tlsf_remove_pool(tlsf_t tlsf, pool_t pool); /* malloc/memalign/realloc/free replacements. */ +#ifdef DBG_TLSF_MALLOC +void* tlsf_malloc(tlsf_t tlsf, size_t size, + const char *file, const char *function, unsigned long line); +void* tlsf_realloc(tlsf_t tlsf, void* ptr, size_t size, + const char *file, const char *function, unsigned long line); +void tlsf_free(tlsf_t tlsf, void* ptr, + const char *file, const char *function, unsigned long line); +#else void* tlsf_malloc(tlsf_t tlsf, size_t bytes); -void* tlsf_memalign(tlsf_t tlsf, size_t align, size_t bytes); void* tlsf_realloc(tlsf_t tlsf, void* ptr, size_t size); void tlsf_free(tlsf_t tlsf, void* ptr); +#endif /* Returns internal block size, not original request size */ size_t tlsf_block_size(void* ptr); @@ -64,6 +72,8 @@ int tlsf_check(tlsf_t tlsf); int tlsf_check_pool(pool_t pool); void tlsf_meminfo(tlsf_t pool, struct mem_info *info); +void tlsf_status(tlsf_t pool); +void tlsf_sums(tlsf_t pool); size_t tlsf_available(tlsf_t pool); #if defined(__cplusplus)