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"
This commit is contained in:
parent
fdba894d21
commit
9cd8c10950
14
ChangeLog
14
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
|
||||
|
@ -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
|
||||
|
||||
/*************************************************************************/
|
||||
/* */
|
||||
|
@ -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
|
||||
{
|
||||
|
@ -27,6 +27,7 @@
|
||||
#include "ftlist.c"
|
||||
#include "ftoutln.c"
|
||||
#include "ftnames.c"
|
||||
#include "ftdbgmem.c"
|
||||
|
||||
#if 0
|
||||
#include "ftextend.c"
|
||||
|
502
src/base/ftdbgmem.c
Normal file
502
src/base/ftdbgmem.c
Normal file
@ -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 */
|
@ -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 );
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
#
|
||||
|
Loading…
Reference in New Issue
Block a user