source: trunk/poppler/freetype2/src/base/ftdbgmem.c @ 165

Last change on this file since 165 was 165, checked in by Eugene Romanenko, 14 years ago

update to latest freetype cvs, (closes #76)

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