shithub: freetype+ttf2subf

Download patch

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.

git/fs: mount .git/fs: mount/attach disallowed
--- 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 */