Subversion Repositories Kolibri OS

Rev

Go to most recent revision | Blame | Last modification | View Log | Download | RSS feed

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