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"
diff --git a/ChangeLog b/ChangeLog
index dc29feb..808abd3 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,17 @@
+2001-10-22 David Turner <david@freetype.org>
+
+ * 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 <tjk@ams.org>
* 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 b9d0f26..a563db4 100644
--- a/include/freetype/config/ftoption.h
+++ b/include/freetype/config/ftoption.h
@@ -230,6 +230,20 @@
/* #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 c6c13b3..a310914 100644
--- a/src/base/Jamfile
+++ b/src/base/Jamfile
@@ -10,7 +10,8 @@
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 e6b7869..b49096b 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 0000000..35b7b2b
--- /dev/null
+++ b/src/base/ftdbgmem.c
@@ -0,0 +1,502 @@
+#include <ft2build.h>
+#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 <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+
+ 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 <stdarg.h>
+
+
+ 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 0bee111..034cb7d 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 440a2ff..8c3bbd4 100644
--- a/src/base/rules.mk
+++ b/src/base/rules.mk
@@ -39,7 +39,8 @@
$(BASE_)ftobjs.c \
$(BASE_)ftstream.c \
$(BASE_)ftoutln.c \
- $(BASE_)ftnames.c
+ $(BASE_)ftnames.c \
+ $(BASE_)ftdbgmem.c
# Base layer `extensions' sources
#