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