sdk-world v2026.11.05
PS2 SDK - world tier (copyleft-allowed, non-essential packages)
Loading...
Searching...
No Matches
ftdbgmem.c
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
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 */