ref: 3e26d07e60945b2cda301705ede608b924b4b915
parent: 014d57140776b6fd282747d051f787beb4e1197d
author: David Turner <[email protected]>
date: Tue Feb 22 10:38:12 EST 2005
* 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.
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,24 @@
+2005-02-22 David Turner <[email protected]>
+
+ * 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 <[email protected]>
* builds/unix/ltmain.sh: Regenerated with `libtoolize --force
--- a/include/freetype/config/ftoption.h
+++ b/include/freetype/config/ftoption.h
@@ -436,7 +436,7 @@
/* 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 */
/*************************************************************************/
--- a/include/freetype/internal/ftmemory.h
+++ b/include/freetype/internal/ftmemory.h
@@ -260,6 +260,9 @@
#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) ) )
--- a/src/base/ftdbgmem.c
+++ b/src/base/ftdbgmem.c
@@ -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,7 +171,22 @@
};
+ 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,6 +327,7 @@
{
FT_ULong i;
+ FT_DumpMemory( table->memory );
if ( table )
{
@@ -290,7 +334,8 @@
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,9 +529,21 @@
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,6 +577,8 @@
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"
@@ -451,17 +585,41 @@
"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,17 +762,17 @@
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;
@@ -617,12 +779,12 @@
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,
@@ -751,6 +922,42 @@
FT_Free( memory, (void **)block );
}
+
+ 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 */