@ -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_f ree ( 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_f ree ( 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 */