Home | History | Annotate | Download | only in base
      1 /***************************************************************************/
      2 /*                                                                         */
      3 /*  ftdbgmem.c                                                             */
      4 /*                                                                         */
      5 /*    Memory debugger (body).                                              */
      6 /*                                                                         */
      7 /*  Copyright 2001-2006, 2009, 2013 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     FT_Long   leak_count = 0;
    348     FT_ULong  leaks      = 0;
    349 
    350 
    351     FT_DumpMemory( table->memory );
    352 
    353     /* remove all blocks from the table, revealing leaked ones */
    354     for ( i = 0; i < table->size; i++ )
    355     {
    356       FT_MemNode  *pnode = table->buckets + i, next, node = *pnode;
    357 
    358 
    359       while ( node )
    360       {
    361         next       = node->link;
    362         node->link = 0;
    363 
    364         if ( node->size > 0 )
    365         {
    366           printf(
    367             "leaked memory block at address %p, size %8ld in (%s:%ld)\n",
    368             node->address, node->size,
    369             FT_FILENAME( node->source->file_name ),
    370             node->source->line_no );
    371 
    372           leak_count++;
    373           leaks += node->size;
    374 
    375           ft_mem_table_free( table, node->address );
    376         }
    377 
    378         node->address = NULL;
    379         node->size    = 0;
    380 
    381         ft_mem_table_free( table, node );
    382         node = next;
    383       }
    384       table->buckets[i] = 0;
    385     }
    386 
    387     ft_mem_table_free( table, table->buckets );
    388     table->buckets = NULL;
    389 
    390     table->size  = 0;
    391     table->nodes = 0;
    392 
    393     /* remove all sources */
    394     for ( i = 0; i < FT_MEM_SOURCE_BUCKETS; i++ )
    395     {
    396       FT_MemSource  source, next;
    397 
    398 
    399       for ( source = table->sources[i]; source != NULL; source = next )
    400       {
    401         next = source->link;
    402         ft_mem_table_free( table, source );
    403       }
    404 
    405       table->sources[i] = NULL;
    406     }
    407 
    408     printf( "FreeType: total memory allocations = %ld\n",
    409             table->alloc_total );
    410     printf( "FreeType: maximum memory footprint = %ld\n",
    411             table->alloc_max );
    412 
    413     ft_mem_table_free( table, table );
    414 
    415     if ( leak_count > 0 )
    416       ft_mem_debug_panic(
    417         "FreeType: %ld bytes of memory leaked in %ld blocks\n",
    418         leaks, leak_count );
    419 
    420     printf( "FreeType: no memory leaks detected\n" );
    421   }
    422 
    423 
    424   static FT_MemNode*
    425   ft_mem_table_get_nodep( FT_MemTable  table,
    426                           FT_Byte*     address )
    427   {
    428     FT_PtrDist   hash;
    429     FT_MemNode  *pnode, node;
    430 
    431 
    432     hash  = FT_MEM_VAL( address );
    433     pnode = table->buckets + ( hash % table->size );
    434 
    435     for (;;)
    436     {
    437       node = pnode[0];
    438       if ( !node )
    439         break;
    440 
    441       if ( node->address == address )
    442         break;
    443 
    444       pnode = &node->link;
    445     }
    446     return pnode;
    447   }
    448 
    449 
    450   static FT_MemSource
    451   ft_mem_table_get_source( FT_MemTable  table )
    452   {
    453     FT_UInt32     hash;
    454     FT_MemSource  node, *pnode;
    455 
    456 
    457     /* cast to FT_PtrDist first since void* can be larger */
    458     /* than FT_UInt32 and GCC 4.1.1 emits a warning       */
    459     hash  = (FT_UInt32)(FT_PtrDist)(void*)_ft_debug_file +
    460               (FT_UInt32)( 5 * _ft_debug_lineno );
    461     pnode = &table->sources[hash % FT_MEM_SOURCE_BUCKETS];
    462 
    463     for ( ;; )
    464     {
    465       node = *pnode;
    466       if ( node == NULL )
    467         break;
    468 
    469       if ( node->file_name == _ft_debug_file &&
    470            node->line_no   == _ft_debug_lineno   )
    471         goto Exit;
    472 
    473       pnode = &node->link;
    474     }
    475 
    476     node = (FT_MemSource)ft_mem_table_alloc( table, sizeof ( *node ) );
    477     if ( node == NULL )
    478       ft_mem_debug_panic(
    479         "not enough memory to perform memory debugging\n" );
    480 
    481     node->file_name = _ft_debug_file;
    482     node->line_no   = _ft_debug_lineno;
    483 
    484     node->cur_blocks = 0;
    485     node->max_blocks = 0;
    486     node->all_blocks = 0;
    487 
    488     node->cur_size   = 0;
    489     node->max_size   = 0;
    490     node->all_size   = 0;
    491 
    492     node->cur_max    = 0;
    493 
    494     node->link = NULL;
    495     node->hash = hash;
    496     *pnode     = node;
    497 
    498   Exit:
    499     return node;
    500   }
    501 
    502 
    503   static void
    504   ft_mem_table_set( FT_MemTable  table,
    505                     FT_Byte*     address,
    506                     FT_ULong     size,
    507                     FT_Long      delta )
    508   {
    509     FT_MemNode  *pnode, node;
    510 
    511 
    512     if ( table )
    513     {
    514       FT_MemSource  source;
    515 
    516 
    517       pnode = ft_mem_table_get_nodep( table, address );
    518       node  = *pnode;
    519       if ( node )
    520       {
    521         if ( node->size < 0 )
    522         {
    523           /* This block was already freed.  Our memory is now completely */
    524           /* corrupted!                                                  */
    525           /* This can only happen in keep-alive mode.                    */
    526           ft_mem_debug_panic(
    527             "memory heap corrupted (allocating freed block)" );
    528         }
    529         else
    530         {
    531           /* This block was already allocated.  This means that our memory */
    532           /* is also corrupted!                                            */
    533           ft_mem_debug_panic(
    534             "memory heap corrupted (re-allocating allocated block at"
    535             " %p, of size %ld)\n"
    536             "org=%s:%d new=%s:%d\n",
    537             node->address, node->size,
    538             FT_FILENAME( node->source->file_name ), node->source->line_no,
    539             FT_FILENAME( _ft_debug_file ), _ft_debug_lineno );
    540         }
    541       }
    542 
    543       /* we need to create a new node in this table */
    544       node = (FT_MemNode)ft_mem_table_alloc( table, sizeof ( *node ) );
    545       if ( node == NULL )
    546         ft_mem_debug_panic( "not enough memory to run memory tests" );
    547 
    548       node->address = address;
    549       node->size    = size;
    550       node->source  = source = ft_mem_table_get_source( table );
    551 
    552       if ( delta == 0 )
    553       {
    554         /* this is an allocation */
    555         source->all_blocks++;
    556         source->cur_blocks++;
    557         if ( source->cur_blocks > source->max_blocks )
    558           source->max_blocks = source->cur_blocks;
    559       }
    560 
    561       if ( size > (FT_ULong)source->cur_max )
    562         source->cur_max = size;
    563 
    564       if ( delta != 0 )
    565       {
    566         /* we are growing or shrinking a reallocated block */
    567         source->cur_size     += delta;
    568         table->alloc_current += delta;
    569       }
    570       else
    571       {
    572         /* we are allocating a new block */
    573         source->cur_size     += size;
    574         table->alloc_current += size;
    575       }
    576 
    577       source->all_size += size;
    578 
    579       if ( source->cur_size > source->max_size )
    580         source->max_size = source->cur_size;
    581 
    582       node->free_file_name = NULL;
    583       node->free_line_no   = 0;
    584 
    585       node->link = pnode[0];
    586 
    587       pnode[0] = node;
    588       table->nodes++;
    589 
    590       table->alloc_total += size;
    591 
    592       if ( table->alloc_current > table->alloc_max )
    593         table->alloc_max = table->alloc_current;
    594 
    595       if ( table->nodes * 3 < table->size  ||
    596            table->size  * 3 < table->nodes )
    597         ft_mem_table_resize( table );
    598     }
    599   }
    600 
    601 
    602   static void
    603   ft_mem_table_remove( FT_MemTable  table,
    604                        FT_Byte*     address,
    605                        FT_Long      delta )
    606   {
    607     if ( table )
    608     {
    609       FT_MemNode  *pnode, node;
    610 
    611 
    612       pnode = ft_mem_table_get_nodep( table, address );
    613       node  = *pnode;
    614       if ( node )
    615       {
    616         FT_MemSource  source;
    617 
    618 
    619         if ( node->size < 0 )
    620           ft_mem_debug_panic(
    621             "freeing memory block at %p more than once at (%s:%ld)\n"
    622             "block allocated at (%s:%ld) and released at (%s:%ld)",
    623             address,
    624             FT_FILENAME( _ft_debug_file ), _ft_debug_lineno,
    625             FT_FILENAME( node->source->file_name ), node->source->line_no,
    626             FT_FILENAME( node->free_file_name ), node->free_line_no );
    627 
    628         /* scramble the node's content for additional safety */
    629         FT_MEM_SET( address, 0xF3, node->size );
    630 
    631         if ( delta == 0 )
    632         {
    633           source = node->source;
    634 
    635           source->cur_blocks--;
    636           source->cur_size -= node->size;
    637 
    638           table->alloc_current -= node->size;
    639         }
    640 
    641         if ( table->keep_alive )
    642         {
    643           /* we simply invert the node's size to indicate that the node */
    644           /* was freed.                                                 */
    645           node->size           = -node->size;
    646           node->free_file_name = _ft_debug_file;
    647           node->free_line_no   = _ft_debug_lineno;
    648         }
    649         else
    650         {
    651           table->nodes--;
    652 
    653           *pnode = node->link;
    654 
    655           node->size   = 0;
    656           node->source = NULL;
    657 
    658           ft_mem_table_free( table, node );
    659 
    660           if ( table->nodes * 3 < table->size  ||
    661                table->size  * 3 < table->nodes )
    662             ft_mem_table_resize( table );
    663         }
    664       }
    665       else
    666         ft_mem_debug_panic(
    667           "trying to free unknown block at %p in (%s:%ld)\n",
    668           address,
    669           FT_FILENAME( _ft_debug_file ), _ft_debug_lineno );
    670     }
    671   }
    672 
    673 
    674   extern FT_Pointer
    675   ft_mem_debug_alloc( FT_Memory  memory,
    676                       FT_Long    size )
    677   {
    678     FT_MemTable  table = (FT_MemTable)memory->user;
    679     FT_Byte*     block;
    680 
    681 
    682     if ( size <= 0 )
    683       ft_mem_debug_panic( "negative block size allocation (%ld)", size );
    684 
    685     /* return NULL if the maximum number of allocations was reached */
    686     if ( table->bound_count                           &&
    687          table->alloc_count >= table->alloc_count_max )
    688       return NULL;
    689 
    690     /* return NULL if this allocation would overflow the maximum heap size */
    691     if ( table->bound_total                                             &&
    692          table->alloc_total_max - table->alloc_current > (FT_ULong)size )
    693       return NULL;
    694 
    695     block = (FT_Byte *)ft_mem_table_alloc( table, size );
    696     if ( block )
    697     {
    698       ft_mem_table_set( table, block, (FT_ULong)size, 0 );
    699 
    700       table->alloc_count++;
    701     }
    702 
    703     _ft_debug_file   = "<unknown>";
    704     _ft_debug_lineno = 0;
    705 
    706     return (FT_Pointer)block;
    707   }
    708 
    709 
    710   extern void
    711   ft_mem_debug_free( FT_Memory   memory,
    712                      FT_Pointer  block )
    713   {
    714     FT_MemTable  table = (FT_MemTable)memory->user;
    715 
    716 
    717     if ( block == NULL )
    718       ft_mem_debug_panic( "trying to free NULL in (%s:%ld)",
    719                           FT_FILENAME( _ft_debug_file ),
    720                           _ft_debug_lineno );
    721 
    722     ft_mem_table_remove( table, (FT_Byte*)block, 0 );
    723 
    724     if ( !table->keep_alive )
    725       ft_mem_table_free( table, block );
    726 
    727     table->alloc_count--;
    728 
    729     _ft_debug_file   = "<unknown>";
    730     _ft_debug_lineno = 0;
    731   }
    732 
    733 
    734   extern FT_Pointer
    735   ft_mem_debug_realloc( FT_Memory   memory,
    736                         FT_Long     cur_size,
    737                         FT_Long     new_size,
    738                         FT_Pointer  block )
    739   {
    740     FT_MemTable  table = (FT_MemTable)memory->user;
    741     FT_MemNode   node, *pnode;
    742     FT_Pointer   new_block;
    743     FT_Long      delta;
    744 
    745     const char*  file_name = FT_FILENAME( _ft_debug_file );
    746     FT_Long      line_no   = _ft_debug_lineno;
    747 
    748 
    749     /* unlikely, but possible */
    750     if ( new_size == cur_size )
    751       return block;
    752 
    753     /* the following is valid according to ANSI C */
    754 #if 0
    755     if ( block == NULL || cur_size == 0 )
    756       ft_mem_debug_panic( "trying to reallocate NULL in (%s:%ld)",
    757                           file_name, line_no );
    758 #endif
    759 
    760     /* while the following is allowed in ANSI C also, we abort since */
    761     /* such case should be handled by FreeType.                      */
    762     if ( new_size <= 0 )
    763       ft_mem_debug_panic(
    764         "trying to reallocate %p to size 0 (current is %ld) in (%s:%ld)",
    765         block, cur_size, file_name, line_no );
    766 
    767     /* check `cur_size' value */
    768     pnode = ft_mem_table_get_nodep( table, (FT_Byte*)block );
    769     node  = *pnode;
    770     if ( !node )
    771       ft_mem_debug_panic(
    772         "trying to reallocate unknown block at %p in (%s:%ld)",
    773         block, file_name, line_no );
    774 
    775     if ( node->size <= 0 )
    776       ft_mem_debug_panic(
    777         "trying to reallocate freed block at %p in (%s:%ld)",
    778         block, file_name, line_no );
    779 
    780     if ( node->size != cur_size )
    781       ft_mem_debug_panic( "invalid ft_realloc request for %p. cur_size is "
    782                           "%ld instead of %ld in (%s:%ld)",
    783                           block, cur_size, node->size, file_name, line_no );
    784 
    785     /* return NULL if the maximum number of allocations was reached */
    786     if ( table->bound_count                           &&
    787          table->alloc_count >= table->alloc_count_max )
    788       return NULL;
    789 
    790     delta = (FT_Long)( new_size - cur_size );
    791 
    792     /* return NULL if this allocation would overflow the maximum heap size */
    793     if ( delta > 0                                                       &&
    794          table->bound_total                                              &&
    795          table->alloc_current + (FT_ULong)delta > table->alloc_total_max )
    796       return NULL;
    797 
    798     new_block = (FT_Byte *)ft_mem_table_alloc( table, new_size );
    799     if ( new_block == NULL )
    800       return NULL;
    801 
    802     ft_mem_table_set( table, (FT_Byte*)new_block, new_size, delta );
    803 
    804     ft_memcpy( new_block, block, cur_size < new_size ? cur_size : new_size );
    805 
    806     ft_mem_table_remove( table, (FT_Byte*)block, delta );
    807 
    808     _ft_debug_file   = "<unknown>";
    809     _ft_debug_lineno = 0;
    810 
    811     if ( !table->keep_alive )
    812       ft_mem_table_free( table, block );
    813 
    814     return new_block;
    815   }
    816 
    817 
    818   extern FT_Int
    819   ft_mem_debug_init( FT_Memory  memory )
    820   {
    821     FT_MemTable  table;
    822     FT_Int       result = 0;
    823 
    824 
    825     if ( getenv( "FT2_DEBUG_MEMORY" ) )
    826     {
    827       table = ft_mem_table_new( memory );
    828       if ( table )
    829       {
    830         const char*  p;
    831 
    832 
    833         memory->user    = table;
    834         memory->alloc   = ft_mem_debug_alloc;
    835         memory->realloc = ft_mem_debug_realloc;
    836         memory->free    = ft_mem_debug_free;
    837 
    838         p = getenv( "FT2_ALLOC_TOTAL_MAX" );
    839         if ( p != NULL )
    840         {
    841           FT_Long   total_max = ft_atol( p );
    842 
    843 
    844           if ( total_max > 0 )
    845           {
    846             table->bound_total     = 1;
    847             table->alloc_total_max = (FT_ULong)total_max;
    848           }
    849         }
    850 
    851         p = getenv( "FT2_ALLOC_COUNT_MAX" );
    852         if ( p != NULL )
    853         {
    854           FT_Long  total_count = ft_atol( p );
    855 
    856 
    857           if ( total_count > 0 )
    858           {
    859             table->bound_count     = 1;
    860             table->alloc_count_max = (FT_ULong)total_count;
    861           }
    862         }
    863 
    864         p = getenv( "FT2_KEEP_ALIVE" );
    865         if ( p != NULL )
    866         {
    867           FT_Long  keep_alive = ft_atol( p );
    868 
    869 
    870           if ( keep_alive > 0 )
    871             table->keep_alive = 1;
    872         }
    873 
    874         result = 1;
    875       }
    876     }
    877     return result;
    878   }
    879 
    880 
    881   extern void
    882   ft_mem_debug_done( FT_Memory  memory )
    883   {
    884     FT_MemTable  table = (FT_MemTable)memory->user;
    885 
    886 
    887     if ( table )
    888     {
    889       memory->free    = table->free;
    890       memory->realloc = table->realloc;
    891       memory->alloc   = table->alloc;
    892 
    893       ft_mem_table_destroy( table );
    894       memory->user = NULL;
    895     }
    896   }
    897 
    898 
    899 
    900   static int
    901   ft_mem_source_compare( const void*  p1,
    902                          const void*  p2 )
    903   {
    904     FT_MemSource  s1 = *(FT_MemSource*)p1;
    905     FT_MemSource  s2 = *(FT_MemSource*)p2;
    906 
    907 
    908     if ( s2->max_size > s1->max_size )
    909       return 1;
    910     else if ( s2->max_size < s1->max_size )
    911       return -1;
    912     else
    913       return 0;
    914   }
    915 
    916 
    917   extern void
    918   FT_DumpMemory( FT_Memory  memory )
    919   {
    920     FT_MemTable  table = (FT_MemTable)memory->user;
    921 
    922 
    923     if ( table )
    924     {
    925       FT_MemSource*  bucket = table->sources;
    926       FT_MemSource*  limit  = bucket + FT_MEM_SOURCE_BUCKETS;
    927       FT_MemSource*  sources;
    928       FT_UInt        nn, count;
    929       const char*    fmt;
    930 
    931 
    932       count = 0;
    933       for ( ; bucket < limit; bucket++ )
    934       {
    935         FT_MemSource  source = *bucket;
    936 
    937 
    938         for ( ; source; source = source->link )
    939           count++;
    940       }
    941 
    942       sources = (FT_MemSource*)ft_mem_table_alloc(
    943                                  table, sizeof ( *sources ) * count );
    944 
    945       count = 0;
    946       for ( bucket = table->sources; bucket < limit; bucket++ )
    947       {
    948         FT_MemSource  source = *bucket;
    949 
    950 
    951         for ( ; source; source = source->link )
    952           sources[count++] = source;
    953       }
    954 
    955       ft_qsort( sources, count, sizeof ( *sources ), ft_mem_source_compare );
    956 
    957       printf( "FreeType Memory Dump: "
    958               "current=%ld max=%ld total=%ld count=%ld\n",
    959               table->alloc_current, table->alloc_max,
    960               table->alloc_total, table->alloc_count );
    961       printf( " block  block    sizes    sizes    sizes   source\n" );
    962       printf( " count   high      sum  highsum      max   location\n" );
    963       printf( "-------------------------------------------------\n" );
    964 
    965       fmt = "%6ld %6ld %8ld %8ld %8ld %s:%d\n";
    966 
    967       for ( nn = 0; nn < count; nn++ )
    968       {
    969         FT_MemSource  source = sources[nn];
    970 
    971 
    972         printf( fmt,
    973                 source->cur_blocks, source->max_blocks,
    974                 source->cur_size, source->max_size, source->cur_max,
    975                 FT_FILENAME( source->file_name ),
    976                 source->line_no );
    977       }
    978       printf( "------------------------------------------------\n" );
    979 
    980       ft_mem_table_free( table, sources );
    981     }
    982   }
    983 
    984 #else  /* !FT_DEBUG_MEMORY */
    985 
    986   /* ANSI C doesn't like empty source files */
    987   typedef int  _debug_mem_dummy;
    988 
    989 #endif /* !FT_DEBUG_MEMORY */
    990 
    991 
    992 /* END */
    993