tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

ftdbgmem.c (23899B)


      1 /****************************************************************************
      2 *
      3 * ftdbgmem.c
      4 *
      5 *   Memory debugger (body).
      6 *
      7 * Copyright (C) 2001-2025 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 <freetype/internal/ftdebug.h>
     22 #include <freetype/internal/ftmemory.h>
     23 #include <freetype/ftsystem.h>
     24 #include <freetype/fterrors.h>
     25 #include <freetype/fttypes.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_MAX  13845163
    143 
    144 #define FT_FILENAME( x )  ( (x) ? (x) : "unknown file" )
    145 
    146 
    147  /*
    148   * Prime numbers are ugly to handle.  It would be better to implement
    149   * L-Hashing, which is 10% faster and doesn't require divisions.
    150   */
    151  static const FT_Int  ft_mem_primes[] =
    152  {
    153    7,
    154    11,
    155    19,
    156    37,
    157    73,
    158    109,
    159    163,
    160    251,
    161    367,
    162    557,
    163    823,
    164    1237,
    165    1861,
    166    2777,
    167    4177,
    168    6247,
    169    9371,
    170    14057,
    171    21089,
    172    31627,
    173    47431,
    174    71143,
    175    106721,
    176    160073,
    177    240101,
    178    360163,
    179    540217,
    180    810343,
    181    1215497,
    182    1823231,
    183    2734867,
    184    4102283,
    185    6153409,
    186    9230113,
    187    13845163,
    188  };
    189 
    190 
    191  static FT_Long
    192  ft_mem_closest_prime( FT_Long  num )
    193  {
    194    size_t  i;
    195 
    196 
    197    for ( i = 0;
    198          i < sizeof ( ft_mem_primes ) / sizeof ( ft_mem_primes[0] ); i++ )
    199      if ( ft_mem_primes[i] > num )
    200        return ft_mem_primes[i];
    201 
    202    return FT_MEM_SIZE_MAX;
    203  }
    204 
    205 
    206  static void
    207  ft_mem_debug_panic( const char*  fmt,
    208                      ... )
    209  {
    210    va_list  ap;
    211 
    212 
    213    printf( "FreeType.Debug: " );
    214 
    215    va_start( ap, fmt );
    216    vprintf( fmt, ap );
    217    va_end( ap );
    218 
    219    printf( "\n" );
    220    exit( EXIT_FAILURE );
    221  }
    222 
    223 
    224  static FT_Pointer
    225  ft_mem_table_alloc( FT_MemTable  table,
    226                      FT_Long      size )
    227  {
    228    FT_Memory   memory = table->memory;
    229    FT_Pointer  block;
    230 
    231 
    232    memory->user = table->memory_user;
    233    block = table->alloc( memory, size );
    234    memory->user = table;
    235 
    236    return block;
    237  }
    238 
    239 
    240  static void
    241  ft_mem_table_free( FT_MemTable  table,
    242                     FT_Pointer   block )
    243  {
    244    FT_Memory  memory = table->memory;
    245 
    246 
    247    memory->user = table->memory_user;
    248    table->free( memory, block );
    249    memory->user = table;
    250  }
    251 
    252 
    253  static void
    254  ft_mem_table_resize( FT_MemTable  table )
    255  {
    256    FT_Long  new_size;
    257 
    258 
    259    new_size = ft_mem_closest_prime( table->nodes );
    260    if ( new_size != table->size )
    261    {
    262      FT_MemNode*  new_buckets;
    263      FT_Long      i;
    264 
    265 
    266      new_buckets = (FT_MemNode *)
    267                      ft_mem_table_alloc(
    268                        table,
    269                        new_size * (FT_Long)sizeof ( FT_MemNode ) );
    270      if ( !new_buckets )
    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 ) % (FT_PtrDist)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 void
    305  ft_mem_table_destroy( FT_MemTable  table )
    306  {
    307    FT_Long  i;
    308    FT_Long  leak_count = 0;
    309    FT_Long  leaks      = 0;
    310 
    311 
    312    /* remove all blocks from the table, revealing leaked ones */
    313    for ( i = 0; i < table->size; i++ )
    314    {
    315      FT_MemNode  *pnode = table->buckets + i, next, node = *pnode;
    316 
    317 
    318      while ( node )
    319      {
    320        next       = node->link;
    321        node->link = NULL;
    322 
    323        if ( node->size > 0 )
    324        {
    325          printf(
    326            "leaked memory block at address %p, size %8ld in (%s:%ld)\n",
    327            (void*)node->address,
    328            node->size,
    329            FT_FILENAME( node->source->file_name ),
    330            node->source->line_no );
    331 
    332          leak_count++;
    333          leaks += node->size;
    334 
    335          ft_mem_table_free( table, node->address );
    336        }
    337 
    338        node->address = NULL;
    339        node->size    = 0;
    340 
    341        ft_mem_table_free( table, node );
    342        node = next;
    343      }
    344      table->buckets[i] = NULL;
    345    }
    346 
    347    ft_mem_table_free( table, table->buckets );
    348    table->buckets = NULL;
    349 
    350    table->size  = 0;
    351    table->nodes = 0;
    352 
    353    /* remove all sources */
    354    for ( i = 0; i < FT_MEM_SOURCE_BUCKETS; i++ )
    355    {
    356      FT_MemSource  source, next;
    357 
    358 
    359      for ( source = table->sources[i]; source != NULL; source = next )
    360      {
    361        next = source->link;
    362        ft_mem_table_free( table, source );
    363      }
    364 
    365      table->sources[i] = NULL;
    366    }
    367 
    368    printf( "FreeType: total memory allocations = %ld\n",
    369            table->alloc_total );
    370    printf( "FreeType: maximum memory footprint = %ld\n",
    371            table->alloc_max );
    372 
    373    if ( leak_count > 0 )
    374      ft_mem_debug_panic(
    375        "FreeType: %ld bytes of memory leaked in %ld blocks\n",
    376        leaks, leak_count );
    377 
    378    printf( "FreeType: no memory leaks detected\n" );
    379  }
    380 
    381 
    382  static FT_MemNode*
    383  ft_mem_table_get_nodep( FT_MemTable  table,
    384                          FT_Byte*     address )
    385  {
    386    FT_PtrDist   hash;
    387    FT_MemNode  *pnode, node;
    388 
    389 
    390    hash  = FT_MEM_VAL( address );
    391    pnode = table->buckets + ( hash % (FT_PtrDist)table->size );
    392 
    393    for (;;)
    394    {
    395      node = pnode[0];
    396      if ( !node )
    397        break;
    398 
    399      if ( node->address == address )
    400        break;
    401 
    402      pnode = &node->link;
    403    }
    404    return pnode;
    405  }
    406 
    407 
    408  static FT_MemSource
    409  ft_mem_table_get_source( FT_MemTable  table )
    410  {
    411    FT_UInt32     hash;
    412    FT_MemSource  node, *pnode;
    413 
    414 
    415    /* cast to FT_PtrDist first since void* can be larger */
    416    /* than FT_UInt32 and GCC 4.1.1 emits a warning       */
    417    hash  = (FT_UInt32)(FT_PtrDist)(void*)ft_debug_file_ +
    418              (FT_UInt32)( 5 * ft_debug_lineno_ );
    419    pnode = &table->sources[hash % FT_MEM_SOURCE_BUCKETS];
    420 
    421    for (;;)
    422    {
    423      node = *pnode;
    424      if ( !node )
    425        break;
    426 
    427      if ( node->file_name == ft_debug_file_   &&
    428           node->line_no   == ft_debug_lineno_ )
    429        goto Exit;
    430 
    431      pnode = &node->link;
    432    }
    433 
    434    node = (FT_MemSource)ft_mem_table_alloc( table, sizeof ( *node ) );
    435    if ( !node )
    436      ft_mem_debug_panic(
    437        "not enough memory to perform memory debugging\n" );
    438 
    439    node->file_name = ft_debug_file_;
    440    node->line_no   = ft_debug_lineno_;
    441 
    442    node->cur_blocks = 0;
    443    node->max_blocks = 0;
    444    node->all_blocks = 0;
    445 
    446    node->cur_size = 0;
    447    node->max_size = 0;
    448    node->all_size = 0;
    449 
    450    node->cur_max = 0;
    451 
    452    node->link = NULL;
    453    node->hash = hash;
    454    *pnode     = node;
    455 
    456  Exit:
    457    return node;
    458  }
    459 
    460 
    461  static void
    462  ft_mem_table_set( FT_MemTable  table,
    463                    FT_Byte*     address,
    464                    FT_Long      size,
    465                    FT_Long      delta )
    466  {
    467    FT_MemNode  *pnode, node;
    468 
    469 
    470    if ( table )
    471    {
    472      FT_MemSource  source;
    473 
    474 
    475      pnode = ft_mem_table_get_nodep( table, address );
    476      node  = *pnode;
    477      if ( node )
    478      {
    479        if ( node->size < 0 )
    480        {
    481          /* This block was already freed.  Our memory is now completely */
    482          /* corrupted!                                                  */
    483          /* This can only happen in keep-alive mode.                    */
    484          ft_mem_debug_panic(
    485            "memory heap corrupted (allocating freed block)" );
    486        }
    487        else
    488        {
    489          /* This block was already allocated.  This means that our memory */
    490          /* is also corrupted!                                            */
    491          ft_mem_debug_panic(
    492            "memory heap corrupted (re-allocating allocated block at"
    493            " %p, of size %ld)\n"
    494            "org=%s:%d new=%s:%d\n",
    495            node->address, node->size,
    496            FT_FILENAME( node->source->file_name ), node->source->line_no,
    497            FT_FILENAME( ft_debug_file_ ), ft_debug_lineno_ );
    498        }
    499      }
    500 
    501      /* we need to create a new node in this table */
    502      node = (FT_MemNode)ft_mem_table_alloc( table, sizeof ( *node ) );
    503      if ( !node )
    504        ft_mem_debug_panic( "not enough memory to run memory tests" );
    505 
    506      node->address = address;
    507      node->size    = size;
    508      node->source  = source = ft_mem_table_get_source( table );
    509 
    510      if ( delta == 0 )
    511      {
    512        /* this is an allocation */
    513        source->all_blocks++;
    514        source->cur_blocks++;
    515        if ( source->cur_blocks > source->max_blocks )
    516          source->max_blocks = source->cur_blocks;
    517      }
    518 
    519      if ( size > source->cur_max )
    520        source->cur_max = size;
    521 
    522      if ( delta != 0 )
    523      {
    524        /* we are growing or shrinking a reallocated block */
    525        source->cur_size     += delta;
    526        table->alloc_current += delta;
    527      }
    528      else
    529      {
    530        /* we are allocating a new block */
    531        source->cur_size     += size;
    532        table->alloc_current += size;
    533      }
    534 
    535      source->all_size += size;
    536 
    537      if ( source->cur_size > source->max_size )
    538        source->max_size = source->cur_size;
    539 
    540      node->free_file_name = NULL;
    541      node->free_line_no   = 0;
    542 
    543      node->link = pnode[0];
    544 
    545      pnode[0] = node;
    546      table->nodes++;
    547 
    548      table->alloc_total += size;
    549 
    550      if ( table->alloc_current > table->alloc_max )
    551        table->alloc_max = table->alloc_current;
    552 
    553      if ( table->nodes * 3 < table->size  ||
    554           table->size  * 3 < table->nodes )
    555        ft_mem_table_resize( table );
    556    }
    557  }
    558 
    559 
    560  static void
    561  ft_mem_table_remove( FT_MemTable  table,
    562                       FT_Byte*     address,
    563                       FT_Long      delta )
    564  {
    565    if ( table )
    566    {
    567      FT_MemNode  *pnode, node;
    568 
    569 
    570      pnode = ft_mem_table_get_nodep( table, address );
    571      node  = *pnode;
    572      if ( node )
    573      {
    574        FT_MemSource  source;
    575 
    576 
    577        if ( node->size < 0 )
    578          ft_mem_debug_panic(
    579            "freeing memory block at %p more than once\n"
    580            "  at (%s:%ld)!\n"
    581            "  Block was allocated at (%s:%ld)\n"
    582            "  and released at (%s:%ld).",
    583            address,
    584            FT_FILENAME( ft_debug_file_ ), ft_debug_lineno_,
    585            FT_FILENAME( node->source->file_name ), node->source->line_no,
    586            FT_FILENAME( node->free_file_name ), node->free_line_no );
    587 
    588        /* scramble the node's content for additional safety */
    589        FT_MEM_SET( address, 0xF3, node->size );
    590 
    591        if ( delta == 0 )
    592        {
    593          source = node->source;
    594 
    595          source->cur_blocks--;
    596          source->cur_size -= node->size;
    597 
    598          table->alloc_current -= node->size;
    599        }
    600 
    601        if ( table->keep_alive )
    602        {
    603          /* we simply invert the node's size to indicate that the node */
    604          /* was freed.                                                 */
    605          node->size           = -node->size;
    606          node->free_file_name = ft_debug_file_;
    607          node->free_line_no   = ft_debug_lineno_;
    608        }
    609        else
    610        {
    611          table->nodes--;
    612 
    613          *pnode = node->link;
    614 
    615          node->size   = 0;
    616          node->source = NULL;
    617 
    618          ft_mem_table_free( table, node );
    619 
    620          if ( table->nodes * 3 < table->size  ||
    621               table->size  * 3 < table->nodes )
    622            ft_mem_table_resize( table );
    623        }
    624      }
    625      else
    626        ft_mem_debug_panic(
    627          "trying to free unknown block at %p in (%s:%ld)\n",
    628          address,
    629          FT_FILENAME( ft_debug_file_ ), ft_debug_lineno_ );
    630    }
    631  }
    632 
    633 
    634  static FT_Pointer
    635  ft_mem_debug_alloc( FT_Memory  memory,
    636                      FT_Long    size )
    637  {
    638    FT_MemTable  table = (FT_MemTable)memory->user;
    639    FT_Byte*     block;
    640 
    641 
    642    if ( size <= 0 )
    643      ft_mem_debug_panic( "negative block size allocation (%ld)", size );
    644 
    645    /* return NULL if the maximum number of allocations was reached */
    646    if ( table->bound_count                           &&
    647         table->alloc_count >= table->alloc_count_max )
    648      return NULL;
    649 
    650    /* return NULL if this allocation would overflow the maximum heap size */
    651    if ( table->bound_total                                   &&
    652         table->alloc_total_max - table->alloc_current > size )
    653      return NULL;
    654 
    655    block = (FT_Byte *)ft_mem_table_alloc( table, size );
    656    if ( block )
    657    {
    658      ft_mem_table_set( table, block, size, 0 );
    659 
    660      table->alloc_count++;
    661    }
    662 
    663    ft_debug_file_   = "<unknown>";
    664    ft_debug_lineno_ = 0;
    665 
    666    return (FT_Pointer)block;
    667  }
    668 
    669 
    670  static void
    671  ft_mem_debug_free( FT_Memory   memory,
    672                     FT_Pointer  block )
    673  {
    674    FT_MemTable  table = (FT_MemTable)memory->user;
    675 
    676 
    677    if ( !block )
    678      ft_mem_debug_panic( "trying to free NULL in (%s:%ld)",
    679                          FT_FILENAME( ft_debug_file_ ),
    680                          ft_debug_lineno_ );
    681 
    682    ft_mem_table_remove( table, (FT_Byte*)block, 0 );
    683 
    684    if ( !table->keep_alive )
    685      ft_mem_table_free( table, block );
    686 
    687    table->alloc_count--;
    688 
    689    ft_debug_file_   = "<unknown>";
    690    ft_debug_lineno_ = 0;
    691  }
    692 
    693 
    694  static FT_Pointer
    695  ft_mem_debug_realloc( FT_Memory   memory,
    696                        FT_Long     cur_size,
    697                        FT_Long     new_size,
    698                        FT_Pointer  block )
    699  {
    700    FT_MemTable  table = (FT_MemTable)memory->user;
    701    FT_MemNode   node, *pnode;
    702    FT_Pointer   new_block;
    703    FT_Long      delta;
    704 
    705    const char*  file_name = FT_FILENAME( ft_debug_file_ );
    706    FT_Long      line_no   = ft_debug_lineno_;
    707 
    708 
    709    /* unlikely, but possible */
    710    if ( new_size == cur_size )
    711      return block;
    712 
    713    /* the following is valid according to ANSI C */
    714 #if 0
    715    if ( !block || !cur_size )
    716      ft_mem_debug_panic( "trying to reallocate NULL in (%s:%ld)",
    717                          file_name, line_no );
    718 #endif
    719 
    720    /* while the following is allowed in ANSI C also, we abort since */
    721    /* such case should be handled by FreeType.                      */
    722    if ( new_size <= 0 )
    723      ft_mem_debug_panic(
    724        "trying to reallocate %p to size 0 (current is %ld) in (%s:%ld)",
    725        block, cur_size, file_name, line_no );
    726 
    727    /* check `cur_size' value */
    728    pnode = ft_mem_table_get_nodep( table, (FT_Byte*)block );
    729    node  = *pnode;
    730    if ( !node )
    731      ft_mem_debug_panic(
    732        "trying to reallocate unknown block at %p in (%s:%ld)",
    733        block, file_name, line_no );
    734 
    735    if ( node->size <= 0 )
    736      ft_mem_debug_panic(
    737        "trying to reallocate freed block at %p in (%s:%ld)",
    738        block, file_name, line_no );
    739 
    740    if ( node->size != cur_size )
    741      ft_mem_debug_panic( "invalid ft_realloc request for %p. cur_size is "
    742                          "%ld instead of %ld in (%s:%ld)",
    743                          block, cur_size, node->size, file_name, line_no );
    744 
    745    /* return NULL if the maximum number of allocations was reached */
    746    if ( table->bound_count                           &&
    747         table->alloc_count >= table->alloc_count_max )
    748      return NULL;
    749 
    750    delta = new_size - cur_size;
    751 
    752    /* return NULL if this allocation would overflow the maximum heap size */
    753    if ( delta > 0                                             &&
    754         table->bound_total                                    &&
    755         table->alloc_current + delta > table->alloc_total_max )
    756      return NULL;
    757 
    758    new_block = (FT_Pointer)ft_mem_table_alloc( table, new_size );
    759    if ( !new_block )
    760      return NULL;
    761 
    762    ft_mem_table_set( table, (FT_Byte*)new_block, new_size, delta );
    763 
    764    ft_memcpy( new_block, block, cur_size < new_size ? (size_t)cur_size
    765                                                     : (size_t)new_size );
    766 
    767    ft_mem_table_remove( table, (FT_Byte*)block, delta );
    768 
    769    ft_debug_file_   = "<unknown>";
    770    ft_debug_lineno_ = 0;
    771 
    772    if ( !table->keep_alive )
    773      ft_mem_table_free( table, block );
    774 
    775    return new_block;
    776  }
    777 
    778 
    779  extern void
    780  ft_mem_debug_init( FT_Memory  memory )
    781  {
    782    FT_MemTable  table;
    783 
    784 
    785    if ( !ft_getenv( "FT2_DEBUG_MEMORY" ) )
    786      return;
    787 
    788    table = (FT_MemTable)memory->alloc( memory, sizeof ( *table ) );
    789 
    790    if ( table )
    791    {
    792      FT_ZERO( table );
    793 
    794      table->memory      = memory;
    795      table->memory_user = memory->user;
    796      table->alloc       = memory->alloc;
    797      table->realloc     = memory->realloc;
    798      table->free        = memory->free;
    799 
    800      ft_mem_table_resize( table );
    801 
    802      if ( table->size )
    803      {
    804        const char*  p;
    805 
    806 
    807        memory->user    = table;
    808        memory->alloc   = ft_mem_debug_alloc;
    809        memory->realloc = ft_mem_debug_realloc;
    810        memory->free    = ft_mem_debug_free;
    811 
    812        p = ft_getenv( "FT2_ALLOC_TOTAL_MAX" );
    813        if ( p )
    814        {
    815          FT_Long  total_max = ft_strtol( p, NULL, 10 );
    816 
    817 
    818          if ( total_max > 0 )
    819          {
    820            table->bound_total     = 1;
    821            table->alloc_total_max = total_max;
    822          }
    823        }
    824 
    825        p = ft_getenv( "FT2_ALLOC_COUNT_MAX" );
    826        if ( p )
    827        {
    828          FT_Long  total_count = ft_strtol( p, NULL, 10 );
    829 
    830 
    831          if ( total_count > 0 )
    832          {
    833            table->bound_count     = 1;
    834            table->alloc_count_max = total_count;
    835          }
    836        }
    837 
    838        p = ft_getenv( "FT2_KEEP_ALIVE" );
    839        if ( p )
    840        {
    841          FT_Long  keep_alive = ft_strtol( p, NULL, 10 );
    842 
    843 
    844          if ( keep_alive > 0 )
    845            table->keep_alive = 1;
    846        }
    847      }
    848      else
    849        memory->free( memory, table );
    850    }
    851  }
    852 
    853 
    854  extern void
    855  ft_mem_debug_done( FT_Memory  memory )
    856  {
    857    if ( memory->free == ft_mem_debug_free )
    858    {
    859      FT_MemTable  table = (FT_MemTable)memory->user;
    860 
    861 
    862      FT_DumpMemory( memory );
    863 
    864      ft_mem_table_destroy( table );
    865 
    866      memory->free    = table->free;
    867      memory->realloc = table->realloc;
    868      memory->alloc   = table->alloc;
    869      memory->user    = table->memory_user;
    870 
    871      memory->free( memory, table );
    872    }
    873  }
    874 
    875 
    876  FT_COMPARE_DEF( int )
    877  ft_mem_source_compare( const void*  p1,
    878                         const void*  p2 )
    879  {
    880    FT_MemSource  s1 = *(FT_MemSource*)p1;
    881    FT_MemSource  s2 = *(FT_MemSource*)p2;
    882 
    883 
    884    if ( s2->max_size > s1->max_size )
    885      return 1;
    886    else if ( s2->max_size < s1->max_size )
    887      return -1;
    888    else
    889      return 0;
    890  }
    891 
    892 
    893  extern void
    894  FT_DumpMemory( FT_Memory  memory )
    895  {
    896    if ( memory->free == ft_mem_debug_free )
    897    {
    898      FT_MemTable    table = (FT_MemTable)memory->user;
    899      FT_MemSource*  bucket = table->sources;
    900      FT_MemSource*  limit  = bucket + FT_MEM_SOURCE_BUCKETS;
    901      FT_MemSource*  sources;
    902      FT_Int         nn, count;
    903      const char*    fmt;
    904 
    905 
    906      count = 0;
    907      for ( ; bucket < limit; bucket++ )
    908      {
    909        FT_MemSource  source = *bucket;
    910 
    911 
    912        for ( ; source; source = source->link )
    913          count++;
    914      }
    915 
    916      sources = (FT_MemSource*)
    917                  ft_mem_table_alloc(
    918                    table, count * (FT_Long)sizeof ( *sources ) );
    919 
    920      count = 0;
    921      for ( bucket = table->sources; bucket < limit; bucket++ )
    922      {
    923        FT_MemSource  source = *bucket;
    924 
    925 
    926        for ( ; source; source = source->link )
    927          sources[count++] = source;
    928      }
    929 
    930      ft_qsort( sources,
    931                (size_t)count,
    932                sizeof ( *sources ),
    933                ft_mem_source_compare );
    934 
    935      printf( "FreeType Memory Dump: "
    936              "current=%ld max=%ld total=%ld count=%ld\n",
    937              table->alloc_current, table->alloc_max,
    938              table->alloc_total, table->alloc_count );
    939      printf( " block  block    sizes    sizes    sizes   source\n" );
    940      printf( " count   high      sum  highsum      max   location\n" );
    941      printf( "-------------------------------------------------\n" );
    942 
    943      fmt = "%6ld %6ld %8ld %8ld %8ld %s:%d\n";
    944 
    945      for ( nn = 0; nn < count; nn++ )
    946      {
    947        FT_MemSource  source = sources[nn];
    948 
    949 
    950        printf( fmt,
    951                source->cur_blocks, source->max_blocks,
    952                source->cur_size, source->max_size, source->cur_max,
    953                FT_FILENAME( source->file_name ),
    954                source->line_no );
    955      }
    956      printf( "------------------------------------------------\n" );
    957 
    958      ft_mem_table_free( table, sources );
    959    }
    960  }
    961 
    962 #else  /* !FT_DEBUG_MEMORY */
    963 
    964  /* ANSI C doesn't like empty source files */
    965  typedef int  debug_mem_dummy_;
    966 
    967 #endif /* !FT_DEBUG_MEMORY */
    968 
    969 
    970 /* END */