misc/libfreetype/src/base/ftdbgmem.c
changeset 9372 915436ff64ab
parent 9371 f3840de881bd
child 9373 b769a8e38cbd
equal deleted inserted replaced
9371:f3840de881bd 9372:915436ff64ab
     1 /***************************************************************************/
       
     2 /*                                                                         */
       
     3 /*  ftdbgmem.c                                                             */
       
     4 /*                                                                         */
       
     5 /*    Memory debugger (body).                                              */
       
     6 /*                                                                         */
       
     7 /*  Copyright 2001, 2002, 2003, 2004, 2005, 2006, 2009 by                  */
       
     8 /*  David Turner, Robert Wilhelm, and Werner Lemberg.                      */
       
     9 /*                                                                         */
       
    10 /*  This file is part of the FreeType project, and may only be used,       */
       
    11 /*  modified, and distributed under the terms of the FreeType project      */
       
    12 /*  license, LICENSE.TXT.  By continuing to use, modify, or distribute     */
       
    13 /*  this file you indicate that you have read the license and              */
       
    14 /*  understand and accept it fully.                                        */
       
    15 /*                                                                         */
       
    16 /***************************************************************************/
       
    17 
       
    18 
       
    19 #include <ft2build.h>
       
    20 #include FT_CONFIG_CONFIG_H
       
    21 #include FT_INTERNAL_DEBUG_H
       
    22 #include FT_INTERNAL_MEMORY_H
       
    23 #include FT_SYSTEM_H
       
    24 #include FT_ERRORS_H
       
    25 #include FT_TYPES_H
       
    26 
       
    27 
       
    28 #ifdef FT_DEBUG_MEMORY
       
    29 
       
    30 #define  KEEPALIVE /* `Keep alive' means that freed blocks aren't released
       
    31                     * to the heap.  This is useful to detect double-frees
       
    32                     * or weird heap corruption, but it uses large amounts of
       
    33                     * memory, however.
       
    34                     */
       
    35 
       
    36 #include FT_CONFIG_STANDARD_LIBRARY_H
       
    37 
       
    38   FT_BASE_DEF( const char* )  _ft_debug_file   = 0;
       
    39   FT_BASE_DEF( long )         _ft_debug_lineno = 0;
       
    40 
       
    41   extern void
       
    42   FT_DumpMemory( FT_Memory  memory );
       
    43 
       
    44 
       
    45   typedef struct FT_MemSourceRec_*  FT_MemSource;
       
    46   typedef struct FT_MemNodeRec_*    FT_MemNode;
       
    47   typedef struct FT_MemTableRec_*   FT_MemTable;
       
    48 
       
    49 
       
    50 #define FT_MEM_VAL( addr )  ((FT_PtrDist)(FT_Pointer)( addr ))
       
    51 
       
    52   /*
       
    53    *  This structure holds statistics for a single allocation/release
       
    54    *  site.  This is useful to know where memory operations happen the
       
    55    *  most.
       
    56    */
       
    57   typedef struct  FT_MemSourceRec_
       
    58   {
       
    59     const char*   file_name;
       
    60     long          line_no;
       
    61 
       
    62     FT_Long       cur_blocks;   /* current number of allocated blocks */
       
    63     FT_Long       max_blocks;   /* max. number of allocated blocks    */
       
    64     FT_Long       all_blocks;   /* total number of blocks allocated   */
       
    65 
       
    66     FT_Long       cur_size;     /* current cumulative allocated size */
       
    67     FT_Long       max_size;     /* maximum cumulative allocated size */
       
    68     FT_Long       all_size;     /* total cumulative allocated size   */
       
    69 
       
    70     FT_Long       cur_max;      /* current maximum allocated size */
       
    71 
       
    72     FT_UInt32     hash;
       
    73     FT_MemSource  link;
       
    74 
       
    75   } FT_MemSourceRec;
       
    76 
       
    77 
       
    78   /*
       
    79    *  We don't need a resizable array for the memory sources, because
       
    80    *  their number is pretty limited within FreeType.
       
    81    */
       
    82 #define FT_MEM_SOURCE_BUCKETS  128
       
    83 
       
    84   /*
       
    85    *  This structure holds information related to a single allocated
       
    86    *  memory block.  If KEEPALIVE is defined, blocks that are freed by
       
    87    *  FreeType are never released to the system.  Instead, their `size'
       
    88    *  field is set to -size.  This is mainly useful to detect double frees,
       
    89    *  at the price of large memory footprint during execution.
       
    90    */
       
    91   typedef struct  FT_MemNodeRec_
       
    92   {
       
    93     FT_Byte*      address;
       
    94     FT_Long       size;     /* < 0 if the block was freed */
       
    95 
       
    96     FT_MemSource  source;
       
    97 
       
    98 #ifdef KEEPALIVE
       
    99     const char*   free_file_name;
       
   100     FT_Long       free_line_no;
       
   101 #endif
       
   102 
       
   103     FT_MemNode    link;
       
   104 
       
   105   } FT_MemNodeRec;
       
   106 
       
   107 
       
   108   /*
       
   109    *  The global structure, containing compound statistics and all hash
       
   110    *  tables.
       
   111    */
       
   112   typedef struct  FT_MemTableRec_
       
   113   {
       
   114     FT_ULong         size;
       
   115     FT_ULong         nodes;
       
   116     FT_MemNode*      buckets;
       
   117 
       
   118     FT_ULong         alloc_total;
       
   119     FT_ULong         alloc_current;
       
   120     FT_ULong         alloc_max;
       
   121     FT_ULong         alloc_count;
       
   122 
       
   123     FT_Bool          bound_total;
       
   124     FT_ULong         alloc_total_max;
       
   125 
       
   126     FT_Bool          bound_count;
       
   127     FT_ULong         alloc_count_max;
       
   128 
       
   129     FT_MemSource     sources[FT_MEM_SOURCE_BUCKETS];
       
   130 
       
   131     FT_Bool          keep_alive;
       
   132 
       
   133     FT_Memory        memory;
       
   134     FT_Pointer       memory_user;
       
   135     FT_Alloc_Func    alloc;
       
   136     FT_Free_Func     free;
       
   137     FT_Realloc_Func  realloc;
       
   138 
       
   139   } FT_MemTableRec;
       
   140 
       
   141 
       
   142 #define FT_MEM_SIZE_MIN  7
       
   143 #define FT_MEM_SIZE_MAX  13845163
       
   144 
       
   145 #define FT_FILENAME( x )  ((x) ? (x) : "unknown file")
       
   146 
       
   147 
       
   148   /*
       
   149    *  Prime numbers are ugly to handle.  It would be better to implement
       
   150    *  L-Hashing, which is 10% faster and doesn't require divisions.
       
   151    */
       
   152   static const FT_UInt  ft_mem_primes[] =
       
   153   {
       
   154     7,
       
   155     11,
       
   156     19,
       
   157     37,
       
   158     73,
       
   159     109,
       
   160     163,
       
   161     251,
       
   162     367,
       
   163     557,
       
   164     823,
       
   165     1237,
       
   166     1861,
       
   167     2777,
       
   168     4177,
       
   169     6247,
       
   170     9371,
       
   171     14057,
       
   172     21089,
       
   173     31627,
       
   174     47431,
       
   175     71143,
       
   176     106721,
       
   177     160073,
       
   178     240101,
       
   179     360163,
       
   180     540217,
       
   181     810343,
       
   182     1215497,
       
   183     1823231,
       
   184     2734867,
       
   185     4102283,
       
   186     6153409,
       
   187     9230113,
       
   188     13845163,
       
   189   };
       
   190 
       
   191 
       
   192   static FT_ULong
       
   193   ft_mem_closest_prime( FT_ULong  num )
       
   194   {
       
   195     FT_UInt  i;
       
   196 
       
   197 
       
   198     for ( i = 0;
       
   199           i < sizeof ( ft_mem_primes ) / sizeof ( ft_mem_primes[0] ); i++ )
       
   200       if ( ft_mem_primes[i] > num )
       
   201         return ft_mem_primes[i];
       
   202 
       
   203     return FT_MEM_SIZE_MAX;
       
   204   }
       
   205 
       
   206 
       
   207   extern void
       
   208   ft_mem_debug_panic( const char*  fmt,
       
   209                       ... )
       
   210   {
       
   211     va_list  ap;
       
   212 
       
   213 
       
   214     printf( "FreeType.Debug: " );
       
   215 
       
   216     va_start( ap, fmt );
       
   217     vprintf( fmt, ap );
       
   218     va_end( ap );
       
   219 
       
   220     printf( "\n" );
       
   221     exit( EXIT_FAILURE );
       
   222   }
       
   223 
       
   224 
       
   225   static FT_Pointer
       
   226   ft_mem_table_alloc( FT_MemTable  table,
       
   227                       FT_Long      size )
       
   228   {
       
   229     FT_Memory   memory = table->memory;
       
   230     FT_Pointer  block;
       
   231 
       
   232 
       
   233     memory->user = table->memory_user;
       
   234     block = table->alloc( memory, size );
       
   235     memory->user = table;
       
   236 
       
   237     return block;
       
   238   }
       
   239 
       
   240 
       
   241   static void
       
   242   ft_mem_table_free( FT_MemTable  table,
       
   243                      FT_Pointer   block )
       
   244   {
       
   245     FT_Memory  memory = table->memory;
       
   246 
       
   247 
       
   248     memory->user = table->memory_user;
       
   249     table->free( memory, block );
       
   250     memory->user = table;
       
   251   }
       
   252 
       
   253 
       
   254   static void
       
   255   ft_mem_table_resize( FT_MemTable  table )
       
   256   {
       
   257     FT_ULong  new_size;
       
   258 
       
   259 
       
   260     new_size = ft_mem_closest_prime( table->nodes );
       
   261     if ( new_size != table->size )
       
   262     {
       
   263       FT_MemNode*  new_buckets;
       
   264       FT_ULong     i;
       
   265 
       
   266 
       
   267       new_buckets = (FT_MemNode *)
       
   268                       ft_mem_table_alloc( table,
       
   269                                           new_size * sizeof ( FT_MemNode ) );
       
   270       if ( new_buckets == NULL )
       
   271         return;
       
   272 
       
   273       FT_ARRAY_ZERO( new_buckets, new_size );
       
   274 
       
   275       for ( i = 0; i < table->size; i++ )
       
   276       {
       
   277         FT_MemNode  node, next, *pnode;
       
   278         FT_PtrDist  hash;
       
   279 
       
   280 
       
   281         node = table->buckets[i];
       
   282         while ( node )
       
   283         {
       
   284           next  = node->link;
       
   285           hash  = FT_MEM_VAL( node->address ) % new_size;
       
   286           pnode = new_buckets + hash;
       
   287 
       
   288           node->link = pnode[0];
       
   289           pnode[0]   = node;
       
   290 
       
   291           node = next;
       
   292         }
       
   293       }
       
   294 
       
   295       if ( table->buckets )
       
   296         ft_mem_table_free( table, table->buckets );
       
   297 
       
   298       table->buckets = new_buckets;
       
   299       table->size    = new_size;
       
   300     }
       
   301   }
       
   302 
       
   303 
       
   304   static FT_MemTable
       
   305   ft_mem_table_new( FT_Memory  memory )
       
   306   {
       
   307     FT_MemTable  table;
       
   308 
       
   309 
       
   310     table = (FT_MemTable)memory->alloc( memory, sizeof ( *table ) );
       
   311     if ( table == NULL )
       
   312       goto Exit;
       
   313 
       
   314     FT_ZERO( table );
       
   315 
       
   316     table->size  = FT_MEM_SIZE_MIN;
       
   317     table->nodes = 0;
       
   318 
       
   319     table->memory = memory;
       
   320 
       
   321     table->memory_user = memory->user;
       
   322 
       
   323     table->alloc   = memory->alloc;
       
   324     table->realloc = memory->realloc;
       
   325     table->free    = memory->free;
       
   326 
       
   327     table->buckets = (FT_MemNode *)
       
   328                        memory->alloc( memory,
       
   329                                       table->size * sizeof ( FT_MemNode ) );
       
   330     if ( table->buckets )
       
   331       FT_ARRAY_ZERO( table->buckets, table->size );
       
   332     else
       
   333     {
       
   334       memory->free( memory, table );
       
   335       table = NULL;
       
   336     }
       
   337 
       
   338   Exit:
       
   339     return table;
       
   340   }
       
   341 
       
   342 
       
   343   static void
       
   344   ft_mem_table_destroy( FT_MemTable  table )
       
   345   {
       
   346     FT_ULong  i;
       
   347 
       
   348 
       
   349     FT_DumpMemory( table->memory );
       
   350 
       
   351     if ( table )
       
   352     {
       
   353       FT_Long   leak_count = 0;
       
   354       FT_ULong  leaks      = 0;
       
   355 
       
   356 
       
   357       /* remove all blocks from the table, revealing leaked ones */
       
   358       for ( i = 0; i < table->size; i++ )
       
   359       {
       
   360         FT_MemNode  *pnode = table->buckets + i, next, node = *pnode;
       
   361 
       
   362 
       
   363         while ( node )
       
   364         {
       
   365           next       = node->link;
       
   366           node->link = 0;
       
   367 
       
   368           if ( node->size > 0 )
       
   369           {
       
   370             printf(
       
   371               "leaked memory block at address %p, size %8ld in (%s:%ld)\n",
       
   372               node->address, node->size,
       
   373               FT_FILENAME( node->source->file_name ),
       
   374               node->source->line_no );
       
   375 
       
   376             leak_count++;
       
   377             leaks += node->size;
       
   378 
       
   379             ft_mem_table_free( table, node->address );
       
   380           }
       
   381 
       
   382           node->address = NULL;
       
   383           node->size    = 0;
       
   384 
       
   385           ft_mem_table_free( table, node );
       
   386           node = next;
       
   387         }
       
   388         table->buckets[i] = 0;
       
   389       }
       
   390 
       
   391       ft_mem_table_free( table, table->buckets );
       
   392       table->buckets = NULL;
       
   393 
       
   394       table->size  = 0;
       
   395       table->nodes = 0;
       
   396 
       
   397       /* remove all sources */
       
   398       for ( i = 0; i < FT_MEM_SOURCE_BUCKETS; i++ )
       
   399       {
       
   400         FT_MemSource  source, next;
       
   401 
       
   402 
       
   403         for ( source = table->sources[i]; source != NULL; source = next )
       
   404         {
       
   405           next = source->link;
       
   406           ft_mem_table_free( table, source );
       
   407         }
       
   408 
       
   409         table->sources[i] = NULL;
       
   410       }
       
   411 
       
   412       printf(
       
   413         "FreeType: total memory allocations = %ld\n", table->alloc_total );
       
   414       printf(
       
   415         "FreeType: maximum memory footprint = %ld\n", table->alloc_max );
       
   416 
       
   417       ft_mem_table_free( table, table );
       
   418 
       
   419       if ( leak_count > 0 )
       
   420         ft_mem_debug_panic(
       
   421           "FreeType: %ld bytes of memory leaked in %ld blocks\n",
       
   422           leaks, leak_count );
       
   423 
       
   424       printf( "FreeType: no memory leaks detected\n" );
       
   425     }
       
   426   }
       
   427 
       
   428 
       
   429   static FT_MemNode*
       
   430   ft_mem_table_get_nodep( FT_MemTable  table,
       
   431                           FT_Byte*     address )
       
   432   {
       
   433     FT_PtrDist   hash;
       
   434     FT_MemNode  *pnode, node;
       
   435 
       
   436 
       
   437     hash  = FT_MEM_VAL( address );
       
   438     pnode = table->buckets + ( hash % table->size );
       
   439 
       
   440     for (;;)
       
   441     {
       
   442       node = pnode[0];
       
   443       if ( !node )
       
   444         break;
       
   445 
       
   446       if ( node->address == address )
       
   447         break;
       
   448 
       
   449       pnode = &node->link;
       
   450     }
       
   451     return pnode;
       
   452   }
       
   453 
       
   454 
       
   455   static FT_MemSource
       
   456   ft_mem_table_get_source( FT_MemTable  table )
       
   457   {
       
   458     FT_UInt32     hash;
       
   459     FT_MemSource  node, *pnode;
       
   460 
       
   461 
       
   462     /* cast to FT_PtrDist first since void* can be larger */
       
   463     /* than FT_UInt32 and GCC 4.1.1 emits a warning       */
       
   464     hash  = (FT_UInt32)(FT_PtrDist)(void*)_ft_debug_file +
       
   465               (FT_UInt32)( 5 * _ft_debug_lineno );
       
   466     pnode = &table->sources[hash % FT_MEM_SOURCE_BUCKETS];
       
   467 
       
   468     for ( ;; )
       
   469     {
       
   470       node = *pnode;
       
   471       if ( node == NULL )
       
   472         break;
       
   473 
       
   474       if ( node->file_name == _ft_debug_file &&
       
   475            node->line_no   == _ft_debug_lineno   )
       
   476         goto Exit;
       
   477 
       
   478       pnode = &node->link;
       
   479     }
       
   480 
       
   481     node = (FT_MemSource)ft_mem_table_alloc( table, sizeof ( *node ) );
       
   482     if ( node == NULL )
       
   483       ft_mem_debug_panic(
       
   484         "not enough memory to perform memory debugging\n" );
       
   485 
       
   486     node->file_name = _ft_debug_file;
       
   487     node->line_no   = _ft_debug_lineno;
       
   488 
       
   489     node->cur_blocks = 0;
       
   490     node->max_blocks = 0;
       
   491     node->all_blocks = 0;
       
   492 
       
   493     node->cur_size   = 0;
       
   494     node->max_size   = 0;
       
   495     node->all_size   = 0;
       
   496 
       
   497     node->cur_max    = 0;
       
   498 
       
   499     node->link = NULL;
       
   500     node->hash = hash;
       
   501     *pnode     = node;
       
   502 
       
   503   Exit:
       
   504     return node;
       
   505   }
       
   506 
       
   507 
       
   508   static void
       
   509   ft_mem_table_set( FT_MemTable  table,
       
   510                     FT_Byte*     address,
       
   511                     FT_ULong     size,
       
   512                     FT_Long      delta )
       
   513   {
       
   514     FT_MemNode  *pnode, node;
       
   515 
       
   516 
       
   517     if ( table )
       
   518     {
       
   519       FT_MemSource  source;
       
   520 
       
   521 
       
   522       pnode = ft_mem_table_get_nodep( table, address );
       
   523       node  = *pnode;
       
   524       if ( node )
       
   525       {
       
   526         if ( node->size < 0 )
       
   527         {
       
   528           /* This block was already freed.  Our memory is now completely */
       
   529           /* corrupted!                                                  */
       
   530           /* This can only happen in keep-alive mode.                    */
       
   531           ft_mem_debug_panic(
       
   532             "memory heap corrupted (allocating freed block)" );
       
   533         }
       
   534         else
       
   535         {
       
   536           /* This block was already allocated.  This means that our memory */
       
   537           /* is also corrupted!                                            */
       
   538           ft_mem_debug_panic(
       
   539             "memory heap corrupted (re-allocating allocated block at"
       
   540             " %p, of size %ld)\n"
       
   541             "org=%s:%d new=%s:%d\n",
       
   542             node->address, node->size,
       
   543             FT_FILENAME( node->source->file_name ), node->source->line_no,
       
   544             FT_FILENAME( _ft_debug_file ), _ft_debug_lineno );
       
   545         }
       
   546       }
       
   547 
       
   548       /* we need to create a new node in this table */
       
   549       node = (FT_MemNode)ft_mem_table_alloc( table, sizeof ( *node ) );
       
   550       if ( node == NULL )
       
   551         ft_mem_debug_panic( "not enough memory to run memory tests" );
       
   552 
       
   553       node->address = address;
       
   554       node->size    = size;
       
   555       node->source  = source = ft_mem_table_get_source( table );
       
   556 
       
   557       if ( delta == 0 )
       
   558       {
       
   559         /* this is an allocation */
       
   560         source->all_blocks++;
       
   561         source->cur_blocks++;
       
   562         if ( source->cur_blocks > source->max_blocks )
       
   563           source->max_blocks = source->cur_blocks;
       
   564       }
       
   565 
       
   566       if ( size > (FT_ULong)source->cur_max )
       
   567         source->cur_max = size;
       
   568 
       
   569       if ( delta != 0 )
       
   570       {
       
   571         /* we are growing or shrinking a reallocated block */
       
   572         source->cur_size     += delta;
       
   573         table->alloc_current += delta;
       
   574       }
       
   575       else
       
   576       {
       
   577         /* we are allocating a new block */
       
   578         source->cur_size     += size;
       
   579         table->alloc_current += size;
       
   580       }
       
   581 
       
   582       source->all_size += size;
       
   583 
       
   584       if ( source->cur_size > source->max_size )
       
   585         source->max_size = source->cur_size;
       
   586 
       
   587       node->free_file_name = NULL;
       
   588       node->free_line_no   = 0;
       
   589 
       
   590       node->link = pnode[0];
       
   591 
       
   592       pnode[0] = node;
       
   593       table->nodes++;
       
   594 
       
   595       table->alloc_total += size;
       
   596 
       
   597       if ( table->alloc_current > table->alloc_max )
       
   598         table->alloc_max = table->alloc_current;
       
   599 
       
   600       if ( table->nodes * 3 < table->size  ||
       
   601            table->size  * 3 < table->nodes )
       
   602         ft_mem_table_resize( table );
       
   603     }
       
   604   }
       
   605 
       
   606 
       
   607   static void
       
   608   ft_mem_table_remove( FT_MemTable  table,
       
   609                        FT_Byte*     address,
       
   610                        FT_Long      delta )
       
   611   {
       
   612     if ( table )
       
   613     {
       
   614       FT_MemNode  *pnode, node;
       
   615 
       
   616 
       
   617       pnode = ft_mem_table_get_nodep( table, address );
       
   618       node  = *pnode;
       
   619       if ( node )
       
   620       {
       
   621         FT_MemSource  source;
       
   622 
       
   623 
       
   624         if ( node->size < 0 )
       
   625           ft_mem_debug_panic(
       
   626             "freeing memory block at %p more than once at (%s:%ld)\n"
       
   627             "block allocated at (%s:%ld) and released at (%s:%ld)",
       
   628             address,
       
   629             FT_FILENAME( _ft_debug_file ), _ft_debug_lineno,
       
   630             FT_FILENAME( node->source->file_name ), node->source->line_no,
       
   631             FT_FILENAME( node->free_file_name ), node->free_line_no );
       
   632 
       
   633         /* scramble the node's content for additional safety */
       
   634         FT_MEM_SET( address, 0xF3, node->size );
       
   635 
       
   636         if ( delta == 0 )
       
   637         {
       
   638           source = node->source;
       
   639 
       
   640           source->cur_blocks--;
       
   641           source->cur_size -= node->size;
       
   642 
       
   643           table->alloc_current -= node->size;
       
   644         }
       
   645 
       
   646         if ( table->keep_alive )
       
   647         {
       
   648           /* we simply invert the node's size to indicate that the node */
       
   649           /* was freed.                                                 */
       
   650           node->size           = -node->size;
       
   651           node->free_file_name = _ft_debug_file;
       
   652           node->free_line_no   = _ft_debug_lineno;
       
   653         }
       
   654         else
       
   655         {
       
   656           table->nodes--;
       
   657 
       
   658           *pnode = node->link;
       
   659 
       
   660           node->size   = 0;
       
   661           node->source = NULL;
       
   662 
       
   663           ft_mem_table_free( table, node );
       
   664 
       
   665           if ( table->nodes * 3 < table->size  ||
       
   666                table->size  * 3 < table->nodes )
       
   667             ft_mem_table_resize( table );
       
   668         }
       
   669       }
       
   670       else
       
   671         ft_mem_debug_panic(
       
   672           "trying to free unknown block at %p in (%s:%ld)\n",
       
   673           address,
       
   674           FT_FILENAME( _ft_debug_file ), _ft_debug_lineno );
       
   675     }
       
   676   }
       
   677 
       
   678 
       
   679   extern FT_Pointer
       
   680   ft_mem_debug_alloc( FT_Memory  memory,
       
   681                       FT_Long    size )
       
   682   {
       
   683     FT_MemTable  table = (FT_MemTable)memory->user;
       
   684     FT_Byte*     block;
       
   685 
       
   686 
       
   687     if ( size <= 0 )
       
   688       ft_mem_debug_panic( "negative block size allocation (%ld)", size );
       
   689 
       
   690     /* return NULL if the maximum number of allocations was reached */
       
   691     if ( table->bound_count                           &&
       
   692          table->alloc_count >= table->alloc_count_max )
       
   693       return NULL;
       
   694 
       
   695     /* return NULL if this allocation would overflow the maximum heap size */
       
   696     if ( table->bound_total                                             &&
       
   697          table->alloc_total_max - table->alloc_current > (FT_ULong)size )
       
   698       return NULL;
       
   699 
       
   700     block = (FT_Byte *)ft_mem_table_alloc( table, size );
       
   701     if ( block )
       
   702     {
       
   703       ft_mem_table_set( table, block, (FT_ULong)size, 0 );
       
   704 
       
   705       table->alloc_count++;
       
   706     }
       
   707 
       
   708     _ft_debug_file   = "<unknown>";
       
   709     _ft_debug_lineno = 0;
       
   710 
       
   711     return (FT_Pointer)block;
       
   712   }
       
   713 
       
   714 
       
   715   extern void
       
   716   ft_mem_debug_free( FT_Memory   memory,
       
   717                      FT_Pointer  block )
       
   718   {
       
   719     FT_MemTable  table = (FT_MemTable)memory->user;
       
   720 
       
   721 
       
   722     if ( block == NULL )
       
   723       ft_mem_debug_panic( "trying to free NULL in (%s:%ld)",
       
   724                           FT_FILENAME( _ft_debug_file ),
       
   725                           _ft_debug_lineno );
       
   726 
       
   727     ft_mem_table_remove( table, (FT_Byte*)block, 0 );
       
   728 
       
   729     if ( !table->keep_alive )
       
   730       ft_mem_table_free( table, block );
       
   731 
       
   732     table->alloc_count--;
       
   733 
       
   734     _ft_debug_file   = "<unknown>";
       
   735     _ft_debug_lineno = 0;
       
   736   }
       
   737 
       
   738 
       
   739   extern FT_Pointer
       
   740   ft_mem_debug_realloc( FT_Memory   memory,
       
   741                         FT_Long     cur_size,
       
   742                         FT_Long     new_size,
       
   743                         FT_Pointer  block )
       
   744   {
       
   745     FT_MemTable  table = (FT_MemTable)memory->user;
       
   746     FT_MemNode   node, *pnode;
       
   747     FT_Pointer   new_block;
       
   748     FT_Long      delta;
       
   749 
       
   750     const char*  file_name = FT_FILENAME( _ft_debug_file );
       
   751     FT_Long      line_no   = _ft_debug_lineno;
       
   752 
       
   753 
       
   754     /* unlikely, but possible */
       
   755     if ( new_size == cur_size )
       
   756       return block;
       
   757 
       
   758     /* the following is valid according to ANSI C */
       
   759 #if 0
       
   760     if ( block == NULL || cur_size == 0 )
       
   761       ft_mem_debug_panic( "trying to reallocate NULL in (%s:%ld)",
       
   762                           file_name, line_no );
       
   763 #endif
       
   764 
       
   765     /* while the following is allowed in ANSI C also, we abort since */
       
   766     /* such case should be handled by FreeType.                      */
       
   767     if ( new_size <= 0 )
       
   768       ft_mem_debug_panic(
       
   769         "trying to reallocate %p to size 0 (current is %ld) in (%s:%ld)",
       
   770         block, cur_size, file_name, line_no );
       
   771 
       
   772     /* check `cur_size' value */
       
   773     pnode = ft_mem_table_get_nodep( table, (FT_Byte*)block );
       
   774     node  = *pnode;
       
   775     if ( !node )
       
   776       ft_mem_debug_panic(
       
   777         "trying to reallocate unknown block at %p in (%s:%ld)",
       
   778         block, file_name, line_no );
       
   779 
       
   780     if ( node->size <= 0 )
       
   781       ft_mem_debug_panic(
       
   782         "trying to reallocate freed block at %p in (%s:%ld)",
       
   783         block, file_name, line_no );
       
   784 
       
   785     if ( node->size != cur_size )
       
   786       ft_mem_debug_panic( "invalid ft_realloc request for %p. cur_size is "
       
   787                           "%ld instead of %ld in (%s:%ld)",
       
   788                           block, cur_size, node->size, file_name, line_no );
       
   789 
       
   790     /* return NULL if the maximum number of allocations was reached */
       
   791     if ( table->bound_count                           &&
       
   792          table->alloc_count >= table->alloc_count_max )
       
   793       return NULL;
       
   794 
       
   795     delta = (FT_Long)( new_size - cur_size );
       
   796 
       
   797     /* return NULL if this allocation would overflow the maximum heap size */
       
   798     if ( delta > 0                                                       &&
       
   799          table->bound_total                                              &&
       
   800          table->alloc_current + (FT_ULong)delta > table->alloc_total_max )
       
   801       return NULL;
       
   802 
       
   803     new_block = (FT_Byte *)ft_mem_table_alloc( table, new_size );
       
   804     if ( new_block == NULL )
       
   805       return NULL;
       
   806 
       
   807     ft_mem_table_set( table, (FT_Byte*)new_block, new_size, delta );
       
   808 
       
   809     ft_memcpy( new_block, block, cur_size < new_size ? cur_size : new_size );
       
   810 
       
   811     ft_mem_table_remove( table, (FT_Byte*)block, delta );
       
   812 
       
   813     _ft_debug_file   = "<unknown>";
       
   814     _ft_debug_lineno = 0;
       
   815 
       
   816     if ( !table->keep_alive )
       
   817       ft_mem_table_free( table, block );
       
   818 
       
   819     return new_block;
       
   820   }
       
   821 
       
   822 
       
   823   extern FT_Int
       
   824   ft_mem_debug_init( FT_Memory  memory )
       
   825   {
       
   826     FT_MemTable  table;
       
   827     FT_Int       result = 0;
       
   828 
       
   829 
       
   830     if ( getenv( "FT2_DEBUG_MEMORY" ) )
       
   831     {
       
   832       table = ft_mem_table_new( memory );
       
   833       if ( table )
       
   834       {
       
   835         const char*  p;
       
   836 
       
   837 
       
   838         memory->user    = table;
       
   839         memory->alloc   = ft_mem_debug_alloc;
       
   840         memory->realloc = ft_mem_debug_realloc;
       
   841         memory->free    = ft_mem_debug_free;
       
   842 
       
   843         p = getenv( "FT2_ALLOC_TOTAL_MAX" );
       
   844         if ( p != NULL )
       
   845         {
       
   846           FT_Long   total_max = ft_atol( p );
       
   847 
       
   848 
       
   849           if ( total_max > 0 )
       
   850           {
       
   851             table->bound_total     = 1;
       
   852             table->alloc_total_max = (FT_ULong)total_max;
       
   853           }
       
   854         }
       
   855 
       
   856         p = getenv( "FT2_ALLOC_COUNT_MAX" );
       
   857         if ( p != NULL )
       
   858         {
       
   859           FT_Long  total_count = ft_atol( p );
       
   860 
       
   861 
       
   862           if ( total_count > 0 )
       
   863           {
       
   864             table->bound_count     = 1;
       
   865             table->alloc_count_max = (FT_ULong)total_count;
       
   866           }
       
   867         }
       
   868 
       
   869         p = getenv( "FT2_KEEP_ALIVE" );
       
   870         if ( p != NULL )
       
   871         {
       
   872           FT_Long  keep_alive = ft_atol( p );
       
   873 
       
   874 
       
   875           if ( keep_alive > 0 )
       
   876             table->keep_alive = 1;
       
   877         }
       
   878 
       
   879         result = 1;
       
   880       }
       
   881     }
       
   882     return result;
       
   883   }
       
   884 
       
   885 
       
   886   extern void
       
   887   ft_mem_debug_done( FT_Memory  memory )
       
   888   {
       
   889     FT_MemTable  table = (FT_MemTable)memory->user;
       
   890 
       
   891 
       
   892     if ( table )
       
   893     {
       
   894       memory->free    = table->free;
       
   895       memory->realloc = table->realloc;
       
   896       memory->alloc   = table->alloc;
       
   897 
       
   898       ft_mem_table_destroy( table );
       
   899       memory->user = NULL;
       
   900     }
       
   901   }
       
   902 
       
   903 
       
   904 
       
   905   static int
       
   906   ft_mem_source_compare( const void*  p1,
       
   907                          const void*  p2 )
       
   908   {
       
   909     FT_MemSource  s1 = *(FT_MemSource*)p1;
       
   910     FT_MemSource  s2 = *(FT_MemSource*)p2;
       
   911 
       
   912 
       
   913     if ( s2->max_size > s1->max_size )
       
   914       return 1;
       
   915     else if ( s2->max_size < s1->max_size )
       
   916       return -1;
       
   917     else
       
   918       return 0;
       
   919   }
       
   920 
       
   921 
       
   922   extern void
       
   923   FT_DumpMemory( FT_Memory  memory )
       
   924   {
       
   925     FT_MemTable  table = (FT_MemTable)memory->user;
       
   926 
       
   927 
       
   928     if ( table )
       
   929     {
       
   930       FT_MemSource*  bucket = table->sources;
       
   931       FT_MemSource*  limit  = bucket + FT_MEM_SOURCE_BUCKETS;
       
   932       FT_MemSource*  sources;
       
   933       FT_UInt        nn, count;
       
   934       const char*    fmt;
       
   935 
       
   936 
       
   937       count = 0;
       
   938       for ( ; bucket < limit; bucket++ )
       
   939       {
       
   940         FT_MemSource  source = *bucket;
       
   941 
       
   942 
       
   943         for ( ; source; source = source->link )
       
   944           count++;
       
   945       }
       
   946 
       
   947       sources = (FT_MemSource*)ft_mem_table_alloc(
       
   948                                  table, sizeof ( *sources ) * count );
       
   949 
       
   950       count = 0;
       
   951       for ( bucket = table->sources; bucket < limit; bucket++ )
       
   952       {
       
   953         FT_MemSource  source = *bucket;
       
   954 
       
   955 
       
   956         for ( ; source; source = source->link )
       
   957           sources[count++] = source;
       
   958       }
       
   959 
       
   960       ft_qsort( sources, count, sizeof ( *sources ), ft_mem_source_compare );
       
   961 
       
   962       printf( "FreeType Memory Dump: "
       
   963               "current=%ld max=%ld total=%ld count=%ld\n",
       
   964               table->alloc_current, table->alloc_max,
       
   965               table->alloc_total, table->alloc_count );
       
   966       printf( " block  block    sizes    sizes    sizes   source\n" );
       
   967       printf( " count   high      sum  highsum      max   location\n" );
       
   968       printf( "-------------------------------------------------\n" );
       
   969 
       
   970       fmt = "%6ld %6ld %8ld %8ld %8ld %s:%d\n";
       
   971 
       
   972       for ( nn = 0; nn < count; nn++ )
       
   973       {
       
   974         FT_MemSource  source = sources[nn];
       
   975 
       
   976 
       
   977         printf( fmt,
       
   978                 source->cur_blocks, source->max_blocks,
       
   979                 source->cur_size, source->max_size, source->cur_max,
       
   980                 FT_FILENAME( source->file_name ),
       
   981                 source->line_no );
       
   982       }
       
   983       printf( "------------------------------------------------\n" );
       
   984 
       
   985       ft_mem_table_free( table, sources );
       
   986     }
       
   987   }
       
   988 
       
   989 #else  /* !FT_DEBUG_MEMORY */
       
   990 
       
   991   /* ANSI C doesn't like empty source files */
       
   992   typedef int  _debug_mem_dummy;
       
   993 
       
   994 #endif /* !FT_DEBUG_MEMORY */
       
   995 
       
   996 
       
   997 /* END */