* src/base/ftdbgmem.c: adding the ability to list all allocation sites

in the memory debugger. Also a new function FT_DumpMemory() was added.
	It is only available in builds with FT_DEBUG_MEMORY defined, and you
	must declare it in your own code to use it, i.e. with something
	like:

	  extern void FT_DumpMemory( FT_Memory );

	  ...

	  FT_DumpMemory( memory );

	* include/freetype/config/ftoptions.h: disabling TrueType bytecode
	interpreter !

	* include/freetype/internal/ftmemory.h: adding FT_ARRAY_ZERO, as a
	convenience macro.
This commit is contained in:
David Turner 2005-02-22 15:38:12 +00:00
parent 957ad87d43
commit 5a5783e9c0
4 changed files with 286 additions and 55 deletions

View File

@ -1,3 +1,24 @@
2005-02-22 David Turner <david@freetype.org>
* src/base/ftdbgmem.c: adding the ability to list all allocation sites
in the memory debugger. Also a new function FT_DumpMemory() was added.
It is only available in builds with FT_DEBUG_MEMORY defined, and you
must declare it in your own code to use it, i.e. with something
like:
extern void FT_DumpMemory( FT_Memory );
...
FT_DumpMemory( memory );
* include/freetype/config/ftoptions.h: disabling TrueType bytecode
interpreter !
* include/freetype/internal/ftmemory.h: adding FT_ARRAY_ZERO, as a
convenience macro.
2005-02-20 Werner Lemberg <wl@gnu.org>
* builds/unix/ltmain.sh: Regenerated with `libtoolize --force

View File

@ -436,7 +436,7 @@ FT_BEGIN_HEADER
/* Do not #undef this macro here, since the build system might */
/* define it for certain configurations only. */
/* */
#define TT_CONFIG_OPTION_BYTECODE_INTERPRETER
/* #define TT_CONFIG_OPTION_BYTECODE_INTERPRETER */
/*************************************************************************/

View File

@ -260,6 +260,9 @@ FT_BEGIN_HEADER
#define FT_ZERO( p ) FT_MEM_ZERO( p, sizeof ( *(p) ) )
#define FT_ARRAY_ZERO( dest, count ) \
FT_MEM_ZERO( dest, (count)*sizeof( *(dest) ) )
#define FT_ARRAY_COPY( dest, source, count ) \
FT_MEM_COPY( dest, source, (count) * sizeof( *(dest) ) )

View File

@ -27,28 +27,63 @@
#ifdef FT_DEBUG_MEMORY
#define KEEPALIVE /* keep-alive means that free-d blocks aren't released
* to the heap. This is useful to detect double-frees
* or weird heap corruption, but it will use gobs of
* memory however.
*/
#include <stdio.h>
#include <stdlib.h>
extern void
FT_DumpMemory( FT_Memory memory );
typedef struct FT_MemNodeRec_* FT_MemNode;
typedef struct FT_MemTableRec_* FT_MemTable;
typedef struct FT_MemSourceRec_* FT_MemSource;
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_MemSourceRec_
{
const char* file_name;
long line_no;
FT_Long cur_blocks; /* current number of allocated blocks */
FT_Long max_blocks; /* max. number of allocated blocks */
FT_Long all_blocks; /* total number of blocks allocated */
FT_Long cur_size; /* current cumulative allocated size */
FT_Long max_size; /* maximum cumulative allocated size */
FT_Long all_size; /* total cumulative allocated size */
FT_Long cur_max; /* current maximum allocated size */
FT_UInt32 hash;
FT_MemSource link;
} FT_MemSourceRec;
/* we don't need a resizable array for the memory sources, because
* their number is pretty limited within FreeType.
*/
#define FT_MEM_SOURCE_BUCKETS 128
typedef struct FT_MemNodeRec_
{
FT_Byte* address;
FT_Long size; /* < 0 if the block was freed */
FT_Byte* address;
FT_Long size; /* < 0 if the block was freed */
const char* alloc_file_name;
FT_Long alloc_line_no;
FT_MemSource source;
const char* free_file_name;
FT_Long free_line_no;
#ifdef KEEPALIVE
const char* free_file_name;
FT_Long free_line_no;
#endif
FT_MemNode link;
FT_MemNode link;
} FT_MemNodeRec;
@ -64,15 +99,19 @@
FT_ULong alloc_max;
FT_ULong alloc_count;
FT_Bool bound_total;
FT_Bool bound_total;
FT_ULong alloc_total_max;
FT_Bool bound_count;
FT_ULong alloc_count_max;
FT_MemSource sources[ FT_MEM_SOURCE_BUCKETS ];
const char* file_name;
FT_Long line_no;
FT_Bool keep_alive;
FT_Memory memory;
FT_Pointer memory_user;
FT_Alloc_Func alloc;
@ -88,6 +127,10 @@
#define FT_FILENAME( x ) ((x) ? (x) : "unknown file")
/* I hate these prime numbers. I'd better implement L-Hashing
* which is 10% faster and doesn't require divisions, but
* I'm too lazy at the moment.
*/
static const FT_UInt ft_mem_primes[] =
{
7,
@ -128,6 +171,21 @@
};
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;
}
extern void
ft_mem_debug_panic( const char* fmt, ... )
@ -146,21 +204,6 @@
}
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 FT_Pointer
ft_mem_table_alloc( FT_MemTable table,
FT_Long size )
@ -209,7 +252,7 @@
if ( new_buckets == NULL )
return;
FT_MEM_ZERO( new_buckets, sizeof ( FT_MemNode ) * new_size );
FT_ARRAY_ZERO( new_buckets, new_size );
for ( i = 0; i < table->size; i++ )
{
@ -250,7 +293,7 @@
if ( table == NULL )
goto Exit;
FT_MEM_ZERO( table, sizeof ( *table ) );
FT_ZERO( table );
table->size = FT_MEM_SIZE_MIN;
table->nodes = 0;
@ -267,7 +310,7 @@
memory->alloc( memory,
table->size * sizeof ( FT_MemNode ) );
if ( table->buckets )
FT_MEM_ZERO( table->buckets, sizeof ( FT_MemNode ) * table->size );
FT_ARRAY_ZERO( table->buckets, table->size );
else
{
memory->free( memory, table );
@ -284,13 +327,15 @@
{
FT_ULong i;
FT_DumpMemory( table->memory );
if ( table )
{
FT_Long leak_count = 0;
FT_ULong leaks = 0;
/* remove all blocks from the table, revealing leaked ones
*/
for ( i = 0; i < table->size; i++ )
{
FT_MemNode *pnode = table->buckets + i, next, node = *pnode;
@ -306,8 +351,8 @@
printf(
"leaked memory block at address %p, size %8ld in (%s:%ld)\n",
node->address, node->size,
FT_FILENAME( node->alloc_file_name ),
node->alloc_line_no );
FT_FILENAME( node->source->file_name ),
node->source->line_no );
leak_count++;
leaks += node->size;
@ -318,7 +363,7 @@
node->address = NULL;
node->size = 0;
free( node );
ft_mem_table_free( table, node );
node = next;
}
table->buckets[i] = 0;
@ -329,17 +374,33 @@
table->size = 0;
table->nodes = 0;
/* remove all sources
*/
for ( i = 0; i < FT_MEM_SOURCE_BUCKETS; i++ )
{
FT_MemSource source, next;
for ( source = table->sources[i]; source != NULL; source = next )
{
next = source->link;
ft_mem_table_free( table, source );
}
table->sources[i] = NULL;
}
printf(
"FreeType: total memory allocations = %ld\n", table->alloc_total );
printf(
"FreeType: maximum memory footprint = %ld\n", table->alloc_max );
free( table );
ft_mem_table_free( table, table );
if ( leak_count > 0 )
ft_mem_debug_panic(
"FreeType: %ld bytes of memory leaked in %ld blocks\n",
leaks, leak_count );
printf( "FreeType: No memory leaks detected!\n" );
}
}
@ -371,6 +432,55 @@
}
static FT_MemSource
ft_mem_table_get_source( FT_MemTable table )
{
FT_UInt32 hash;
FT_MemSource node, *pnode;
hash = (FT_UInt32)(void*)table->file_name + (FT_UInt32)(5*table->line_no);
pnode = &table->sources[ hash % FT_MEM_SOURCE_BUCKETS ];
for ( ;; )
{
node = *pnode;
if ( node == NULL )
break;
if ( node->file_name == table->file_name &&
node->line_no == table->line_no )
goto Exit;
pnode = &node->link;
}
node = ft_mem_table_alloc( table, sizeof(*node) );
if ( node == NULL )
ft_mem_debug_panic(
"not enough memory to perform memory debugging\n" );
node->file_name = table->file_name;
node->line_no = table->line_no;
node->cur_blocks = 0;
node->max_blocks = 0;
node->all_blocks = 0;
node->cur_size = 0;
node->max_size = 0;
node->all_size = 0;
node->cur_max = 0;
node->link = NULL;
node->hash = hash;
*pnode = node;
Exit:
return node;
}
static void
ft_mem_table_set( FT_MemTable table,
FT_Byte* address,
@ -381,14 +491,19 @@
if ( table )
{
FT_MemSource source;
pnode = ft_mem_table_get_nodep( table, address );
node = *pnode;
if ( node )
{
FT_MemSource source;
if ( node->size < 0 )
{
/* this block was already freed. This means that our memory is */
/* now completely corrupted! */
/* this can only happen in keep-alive mode */
ft_mem_debug_panic(
"memory heap corrupted (allocating freed block)" );
}
@ -397,7 +512,12 @@
/* this block was already allocated. This means that our memory */
/* is also corrupted! */
ft_mem_debug_panic(
"memory heap corrupted (re-allocating allocated block)" );
"memory heap corrupted (re-allocating allocated block at"
" %p, of size %ld)\n"
"org=%s:%d new=%s:%d\n",
node->address, node->size,
FT_FILENAME( node->source->file_name ), node->source->line_no,
FT_FILENAME( table->file_name ), table->line_no );
}
}
@ -409,8 +529,20 @@
node->address = address;
node->size = size;
node->alloc_file_name = table->file_name;
node->alloc_line_no = table->line_no;
node->source = source = ft_mem_table_get_source( table );
source->all_blocks++;
source->cur_blocks++;
if ( source->cur_blocks > source->max_blocks )
source->max_blocks = source->cur_blocks;
if ( size > source->cur_max )
source->cur_max = size;
source->all_size += size;
source->cur_size += size;
if ( source->cur_size > source->max_size )
source->max_size = source->cur_size;
node->free_file_name = NULL;
node->free_line_no = 0;
@ -445,23 +577,49 @@
node = *pnode;
if ( node )
{
FT_MemSource source;
if ( node->size < 0 )
ft_mem_debug_panic(
"freeing memory block at %p more than once at (%s:%ld)\n"
"block allocated at (%s:%ld) and released at (%s:%ld)",
address,
FT_FILENAME( table->file_name ), table->line_no,
FT_FILENAME( node->alloc_file_name ), node->alloc_line_no,
FT_FILENAME( node->source->file_name ), node->source->line_no,
FT_FILENAME( node->free_file_name ), node->free_line_no );
/* we simply invert the node's size to indicate that the node */
/* was freed. We also change its contents. */
/* scramble the node's content for additionnals safety */
FT_MEM_SET( address, 0xF3, node->size );
table->alloc_current -= node->size;
node->size = -node->size;
node->free_file_name = table->file_name;
node->free_line_no = table->line_no;
source = node->source;
source->cur_blocks--;
source->cur_size -= node->size;
if ( table->keep_alive )
{
/* we simply invert the node's size to indicate that the node */
/* was freed. */
node->size = -node->size;
node->free_file_name = table->file_name;
node->free_line_no = table->line_no;
}
else
{
table->nodes --;
*pnode = node->link;
node->size = 0;
node->source = NULL;
ft_mem_table_free( table, node );
if ( table->nodes * 3 < table->size ||
table->size * 3 < table->nodes )
ft_mem_table_resize( table );
}
}
else
ft_mem_debug_panic(
@ -489,9 +647,9 @@
return NULL;
/* return NULL if this allocation would overflow the maximum heap size */
if ( table->bound_total &&
if ( table->bound_total &&
table->alloc_current + (FT_ULong)size > table->alloc_total_max )
return NULL;
return NULL;
block = (FT_Byte *)ft_mem_table_alloc( table, size );
if ( block )
@ -520,7 +678,11 @@
ft_mem_table_remove( table, (FT_Byte*)block );
/* we never really free the block */
if ( !table->keep_alive )
ft_mem_table_free( table, block );
table->alloc_count--;
table->file_name = NULL;
table->line_no = 0;
}
@ -548,7 +710,7 @@
#endif
/* while the following is allowed in ANSI C also, we abort since */
/* such code shouldn't be in FreeType... */
/* such case should be handled by FreeType. */
if ( new_size <= 0 )
ft_mem_debug_panic(
"trying to reallocate %p to size 0 (current is %ld) in (%s:%ld)",
@ -600,29 +762,29 @@
if ( table )
{
const char* p;
memory->user = table;
memory->alloc = ft_mem_debug_alloc;
memory->realloc = ft_mem_debug_realloc;
memory->free = ft_mem_debug_free;
p = getenv( "FT2_ALLOC_TOTAL_MAX" );
if ( p != NULL )
{
FT_Long total_max = ft_atol(p);
if ( total_max > 0 )
{
table->bound_total = 1;
table->alloc_total_max = (FT_ULong) total_max;
}
}
p = getenv( "FT2_ALLOC_COUNT_MAX" );
if ( p != NULL )
{
FT_Long total_count = ft_atol(p);
if ( total_count > 0 )
{
table->bound_count = 1;
@ -630,6 +792,15 @@
}
}
p = getenv( "FT2_KEEP_ALIVE" );
if ( p != NULL )
{
FT_Long keep_alive = ft_atol(p);
if ( keep_alive > 0 )
table->keep_alive = 1;
}
result = 1;
}
}
@ -733,7 +904,7 @@
return FT_QRealloc( memory, current, size, P );
}
FT_BASE_DEF( void )
FT_Free_Debug( FT_Memory memory,
FT_Pointer block,
@ -752,6 +923,42 @@
}
extern void
FT_DumpMemory( FT_Memory memory )
{
FT_MemTable table = (FT_MemTable)memory->user;
if ( table )
{
FT_MemSource* bucket = table->sources;
FT_MemSource* limit = bucket + FT_MEM_SOURCE_BUCKETS;
const char* fmt;
printf( "FreeType Memory Dump: current=%ld max=%ld total=%ld count=%ld\n",
table->alloc_current, table->alloc_max, table->alloc_total, table->alloc_count );
printf( " block block sizes sizes sizes source\n" );
printf( " count high sum highsum max location\n" );
printf( "-------------------------------------------------\n" );
fmt = "%6ld %6ld %10ld %10ld %10ld %s:%d\n";
for ( ; bucket < limit; bucket++ )
{
FT_MemSource source = *bucket;
for ( ; source; source = source->link )
{
printf( fmt,
source->cur_blocks, source->max_blocks,
source->cur_size, source->max_size, source->cur_max,
FT_FILENAME( source->file_name ),
source->line_no );
}
}
printf( "------------------------------------------------\n" );
}
}
#else /* !FT_DEBUG_MEMORY */
/* ANSI C doesn't like empty source files */