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