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