From 869fc49017be8189325efd34a2ab7fd853758d16 Mon Sep 17 00:00:00 2001 From: David Turner Date: Mon, 22 Oct 2001 08:32:21 +0000 Subject: [PATCH] Added a new debugging memory manager implementation. See the FT_DEBUG_MEMORY macro definition in "ftoption.h", as well as the file "src/base/ftdbgmem.c" --- ChangeLog | 14 + include/freetype/config/ftoption.h | 14 + src/base/Jamfile | 3 +- src/base/ftbase.c | 1 + src/base/ftdbgmem.c | 502 +++++++++++++++++++++++++++++ src/base/ftsystem.c | 18 ++ src/base/rules.mk | 3 +- 7 files changed, 553 insertions(+), 2 deletions(-) create mode 100644 src/base/ftdbgmem.c diff --git a/ChangeLog b/ChangeLog index dc29febbb..808abd34f 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,17 @@ +2001-10-22 David Turner + + * src/base/ftdbgmem.c: new debugging memory manager. You must define + the FT_DEBUG_MEMORY macro in "ftoption.h" to enable it. It will record + every memory block allocated and report simple errors like memory + leaks and double deletes. + + * include/freetype/config/ftoption.h: added the FT_DEBUG_MEMORY macro + definition + + * src/base/ftsystem.c (FT_New_Memory, FT_Done_Memory): modified the + base component to use the debugging memory manager when the macro + FT_DEBUG_MEMORY is defined.. + 2001-10-21 Tom Kacvinsky * src/cff/cffload.c (CFF_Done_Font): Free subfonts array only if diff --git a/include/freetype/config/ftoption.h b/include/freetype/config/ftoption.h index b9d0f26cd..a563db409 100644 --- a/include/freetype/config/ftoption.h +++ b/include/freetype/config/ftoption.h @@ -230,6 +230,20 @@ FT_BEGIN_HEADER /* #define FT_DEBUG_LEVEL_ERROR */ /* #define FT_DEBUG_LEVEL_TRACE */ + /*************************************************************************/ + /* */ + /* Memory Debugging */ + /* */ + /* FreeType now comes with an integrated memory debugger that is */ + /* capable of detecting simple errors like memory leaks or double */ + /* deletes. You should define the FT_DEBUG_MEMORY macro to enable */ + /* it.. */ + /* */ + /* beware that when the debugging memory allocator is used, FreeType */ + /* will use a _lot_ more memory. You should always ensure that this */ + /* macro is undefined in release or testing builds.. */ + /* */ +#define FT_DEBUG_MEMORY /*************************************************************************/ /* */ diff --git a/src/base/Jamfile b/src/base/Jamfile index c6c13b315..a31091432 100644 --- a/src/base/Jamfile +++ b/src/base/Jamfile @@ -10,7 +10,8 @@ SubDirHdrs [ FT2_SubDir src base ] ; if $(FT2_MULTI) { - _sources = ftcalc ftextend ftlist ftobjs ftstream ftoutln ftnames fttrigon ; + _sources = ftcalc ftextend ftlist ftobjs ftstream ftoutln ftnames fttrigon + ftdbgmem ; } else { diff --git a/src/base/ftbase.c b/src/base/ftbase.c index e6b7869db..b49096bcf 100644 --- a/src/base/ftbase.c +++ b/src/base/ftbase.c @@ -27,6 +27,7 @@ #include "ftlist.c" #include "ftoutln.c" #include "ftnames.c" +#include "ftdbgmem.c" #if 0 #include "ftextend.c" diff --git a/src/base/ftdbgmem.c b/src/base/ftdbgmem.c new file mode 100644 index 000000000..35b7b2bda --- /dev/null +++ b/src/base/ftdbgmem.c @@ -0,0 +1,502 @@ +#include +#include FT_CONFIG_CONFIG_H +#include FT_INTERNAL_DEBUG_H +#include FT_SYSTEM_H +#include FT_ERRORS_H +#include FT_TYPES_H + +#ifdef FT_DEBUG_MEMORY + + +#include +#include +#include + + + typedef struct FT_MemNodeRec_* FT_MemNode; + typedef struct FT_MemTableRec_* FT_MemTable; + +#define FT_MEM_VAL(addr) ((FT_ULong)(FT_Pointer)(addr)) + + typedef struct FT_MemNodeRec_ + { + FT_Byte* address; + FT_Long size; /* < 0 if the block was freed */ + FT_MemNode link; + + } FT_MemNodeRec; + + typedef struct FT_MemTableRec_ + { + FT_Memory memory; + FT_ULong size; + FT_ULong nodes; + FT_MemNode* buckets; + + FT_ULong alloc_total; + FT_ULong alloc_current; + + } FT_MemTableRec; + +#define FT_MEM_SIZE_MIN 7 +#define FT_MEM_SIZE_MAX 13845163 + + static const FT_UInt ft_mem_primes[] = + { + 7, + 11, + 19, + 37, + 73, + 109, + 163, + 251, + 367, + 557, + 823, + 1237, + 1861, + 2777, + 4177, + 6247, + 9371, + 14057, + 21089, + 31627, + 47431, + 71143, + 106721, + 160073, + 240101, + 360163, + 540217, + 810343, + 1215497, + 1823231, + 2734867, + 4102283, + 6153409, + 9230113, + 13845163, + }; + + +#include + + + extern void + ft_mem_debug_panic( const char* fmt, ... ) + { + va_list ap; + + + printf( "FreeType.DebugMemory: " ); + + va_start( ap, fmt ); + vprintf( fmt, ap ); + va_end( ap ); + + printf( "\n" ); + exit( EXIT_FAILURE ); + } + + + static FT_ULong + ft_mem_closest_prime( FT_ULong num ) + { + FT_UInt i; + + for ( i = 0; i < sizeof(ft_mem_primes)/sizeof(ft_mem_primes[0]); i++ ) + if ( ft_mem_primes[i] > num ) + return ft_mem_primes[i]; + + return FT_MEM_SIZE_MAX; + } + + + + static void + ft_mem_table_resize( FT_MemTable table ) + { + FT_ULong new_size; + + new_size = ft_mem_closest_prime( table->nodes ); + if (new_size != table->size) + { + FT_MemNode* new_buckets ; + FT_ULong i; + + new_buckets = malloc( new_size * sizeof(FT_MemNode) ); + if ( new_buckets == NULL ) + return; + + memset( new_buckets, 0, sizeof(FT_MemNode)*new_size ); + + for ( i = 0; i < table->size; i++ ) + { + FT_MemNode node, next, *pnode; + FT_ULong hash; + + node = table->buckets[i]; + while (node) + { + next = node->link; + hash = FT_MEM_VAL(node->address) % new_size; + pnode = new_buckets + hash; + + node->link = pnode[0]; + pnode[0] = node; + + node = next; + } + } + + if ( table->buckets ) + free( table->buckets ); + + table->buckets = new_buckets; + table->size = new_size; + } + } + + + static FT_MemNode + ft_mem_node_new( FT_MemTable table, + FT_Pointer address, + FT_ULong size ) + { + FT_MemNode node; + + node = malloc( sizeof(*node) ); + if ( node == NULL ) + ft_mem_debug_panic( "not enough memory to run memory tests" ); + + node->link = NULL; + node->address = address; + node->size = size; + + return node; + } + + + static void + ft_mem_node_destroy( FT_MemNode node, + FT_MemTable table ) + { + if (node) + { + node->address = NULL; + node->size = 0; + node->link = NULL; + + free( node ); + } + } + + + + static FT_MemTable + ft_mem_table_new( void ) + { + FT_MemTable table; + + table = malloc( sizeof(*table) ); + if ( table == NULL ) goto Exit; + + memset( table, 0, sizeof(*table) ); + + table->size = FT_MEM_SIZE_MIN; + table->nodes = 0; + + table->buckets = malloc( table->size * sizeof(FT_MemNode) ); + if ( table->buckets ) + memset( table->buckets, 0, sizeof(FT_MemNode)*table->size ); + else + { + free( table ); + table = NULL; + } + + Exit: + return table; + } + + + + static void + ft_mem_table_destroy( FT_MemTable table ) + { + FT_ULong i; + + if ( table ) + { + FT_Long leak_count = 0; + FT_ULong leaks = 0; + + for ( i = 0; i < table->size; i++ ) + { + FT_MemNode *pnode = table->buckets + i, next, node = *pnode; + + while (node) + { + next = node->link; + node->link = 0; + + if ( node->size > 0 ) + { + printf( "leaked memory block at address %p, size %ld\n", + node->address, node->size ); + + leak_count++; + leaks += node->size; + + free( node->address ); + } + + node->address = NULL; + node->size = 0; + + free( node ); + node = next; + } + table->buckets[i] = 0; + } + free( table->buckets ); + table->buckets = NULL; + + table->size = 0; + table->nodes = 0; + free( table ); + + if ( leak_count > 0 ) + ft_mem_debug_panic( "%ld bytes of memory leaked in %ld blocks\n", + leaks, leak_count ); + printf( "no FreeType memory leaks detected !!\n" ); + } + } + + + + static FT_MemNode* + ft_mem_table_get_nodep( FT_MemTable table, + FT_Byte* address ) + { + FT_ULong hash; + FT_MemNode *pnode, node; + + hash = FT_MEM_VAL(address); + pnode = table->buckets + (hash % table->size); + + for (;;) + { + node = pnode[0]; + if (!node) + break; + + if ( node->address == address ) + break; + + pnode = &node->link; + } + return pnode; + } + + + + static void + ft_mem_table_set( FT_MemTable table, + FT_Byte* address, + FT_ULong size ) + { + FT_MemNode *pnode, node; + + if (table) + { + pnode = ft_mem_table_get_nodep( table, address ); + node = *pnode; + if (node) + { + if ( node->size < 0 ) + { + /* this block was already freed. this means that our memory is */ + /* now completely corrupted !! */ + ft_mem_debug_panic( "memory heap corrupted" ); + } + else + { + /* this block was already allocated. this means that our memory */ + /* is also corrupted !! */ + ft_mem_debug_panic( "duplicate block allocation at address " + "%p, size %ld\n", + address, size ); + } + } + + /* we need to create a new node in this table */ + node = malloc( sizeof(*node) ); + if ( node == NULL ) + ft_mem_debug_panic( "not enough memory to run memory tests" ); + + node->address = address; + node->size = size; + node->link = pnode[0]; + + pnode[0] = node; + table->nodes++; + + table->alloc_total += size; + table->alloc_current += size; + + if ( table->nodes*3 < table->size || + table->size *3 < table->nodes ) + ft_mem_table_resize( table ); + } + } + + + static void + ft_mem_table_remove( FT_MemTable table, + FT_Byte* address ) + { + if (table) + { + FT_MemNode *pnode, node; + + pnode = ft_mem_table_get_nodep( table, address ); + node = *pnode; + if (node) + { + if ( node->size < 0 ) + ft_mem_debug_panic( "freeing memory block at %p more than once !!", + address ); + + /* we simply invert the node's size to indicate that the node */ + /* was freed. We also change its content.. */ + memset( address, 0xF3, node->size ); + + table->alloc_current -= node->size; + node->size = -node->size; + } + else + ft_mem_debug_panic( "trying to free unknown block at %p\n", + address ); + } + } + + + extern FT_Pointer + ft_mem_debug_alloc( FT_Memory memory, + FT_Long size ) + { + FT_MemTable table = memory->user; + FT_Byte* block; + + if ( size <= 0 ) + ft_mem_debug_panic( "negative block size allocation (%ld)", size ); + + block = malloc( size ); + if ( block ) + ft_mem_table_set( table, block, (FT_ULong)size ); + + return (FT_Pointer) block; + } + + + extern void + ft_mem_debug_free( FT_Memory memory, + FT_Pointer block ) + { + FT_MemTable table = memory->user; + + if ( block == NULL ) + ft_mem_debug_panic( "trying to free NULL !!" ); + + ft_mem_table_remove( table, (FT_Byte*)block ); + + /* we never really free the block */ + } + + + + extern FT_Pointer + ft_mem_debug_realloc( FT_Memory memory, + FT_Long cur_size, + FT_Long new_size, + FT_Pointer block ) + { + FT_MemTable table = memory->user; + FT_MemNode node, *pnode; + FT_Pointer new_block; + + if ( block == NULL || cur_size == 0 ) + ft_mem_debug_panic( "trying to reallocate NULL" ); + + if ( new_size <= 0 ) + ft_mem_debug_panic( "trying to reallocate %p to size 0 (current is %ld)", + block, cur_size ); + + /* check 'cur_size' value */ + pnode = ft_mem_table_get_nodep( table, (FT_Byte*)block ); + node = *pnode; + if (!node) + ft_mem_debug_panic( "trying to reallocate unknown block at %p", + block ); + + if ( node->size <= 0 ) + ft_mem_debug_panic( "trying to reallocate freed block at %p", + block ); + + if ( node->size != cur_size ) + ft_mem_debug_panic( "invalid realloc request for %p. cur_size is " + "%ld instead of %ld", block, cur_size, node->size ); + + new_block = ft_mem_debug_alloc( memory, new_size ); + if ( new_block == NULL ) + return NULL; + + memcpy( new_block, block, cur_size < new_size ? cur_size : new_size ); + + ft_mem_debug_free( memory, (FT_Byte*)block ); + + return new_block; + } + + + extern FT_Int + ft_mem_debug_init( FT_Memory memory ) + { + FT_MemTable table; + FT_Int result = 0; + + table = ft_mem_table_new(); + if ( table ) + { + memory->user = table; + memory->alloc = ft_mem_debug_alloc; + memory->realloc = ft_mem_debug_realloc; + memory->free = ft_mem_debug_free; + result = 1; + } + return result; + } + + + extern void + ft_mem_debug_done( FT_Memory memory ) + { + FT_MemTable table = memory->user; + + if ( table ) + { + ft_mem_table_destroy( table ); + memory->user = NULL; + memory->free = (FT_Free_Func) free; + } + } + +#else /* !FT_DEBUG_MEMORY */ + + /* ansi C doesn't like empty source files */ + extern const FT_Byte _debug_mem_dummy = 0; + +#endif /* !FT_DEBUG_MEMORY */ diff --git a/src/base/ftsystem.c b/src/base/ftsystem.c index 0bee11110..034cb7d82 100644 --- a/src/base/ftsystem.c +++ b/src/base/ftsystem.c @@ -254,6 +254,18 @@ } + +#ifdef FT_DEBUG_MEMORY + + extern FT_Int + ft_mem_debug_init( FT_Memory memory ); + + extern void + ft_mem_debug_done( FT_Memory memory ); + +#endif + + /* documentation is in ftobjs.h */ FT_EXPORT_DEF( FT_Memory ) @@ -264,6 +276,9 @@ memory = (FT_Memory)malloc( sizeof ( *memory ) ); if ( memory ) +#ifdef FT_DEBUG_MEMORY + if ( !ft_mem_debug_init( memory ) ) +#endif { memory->user = 0; memory->alloc = ft_alloc; @@ -280,6 +295,9 @@ FT_EXPORT_DEF( void ) FT_Done_Memory( FT_Memory memory ) { +#ifdef FT_DEBUG_MEMORY + ft_mem_debug_done( memory ); +#endif memory->free( memory, memory ); } diff --git a/src/base/rules.mk b/src/base/rules.mk index 440a2ffd3..8c3bbd4e9 100644 --- a/src/base/rules.mk +++ b/src/base/rules.mk @@ -39,7 +39,8 @@ BASE_SRC := $(BASE_)ftcalc.c \ $(BASE_)ftobjs.c \ $(BASE_)ftstream.c \ $(BASE_)ftoutln.c \ - $(BASE_)ftnames.c + $(BASE_)ftnames.c \ + $(BASE_)ftdbgmem.c # Base layer `extensions' sources #