* 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:
parent
014d571407
commit
3e26d07e60
21
ChangeLog
21
ChangeLog
@ -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
|
||||
|
@ -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 */
|
||||
|
||||
|
||||
/*************************************************************************/
|
||||
|
@ -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) ) )
|
||||
|
||||
|
@ -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 */
|
||||
|
Loading…
Reference in New Issue
Block a user