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