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 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 208 ft_mem_debug_panic( const char* fmt, 209 ... ) 210 { 211 va_list ap; 212 213 214 printf( "FreeType.Debug: " ); 215 216 va_start( ap, fmt ); 217 vprintf( fmt, ap ); 218 va_end( ap ); 219 220 printf( "\n" ); 221 exit( EXIT_FAILURE ); 222 } 223 224 225 static FT_Pointer 226 ft_mem_table_alloc( FT_MemTable table, 227 FT_Long size ) 228 { 229 FT_Memory memory = table->memory; 230 FT_Pointer block; 231 232 233 memory->user = table->memory_user; 234 block = table->alloc( memory, size ); 235 memory->user = table; 236 237 return block; 238 } 239 240 241 static void 242 ft_mem_table_free( FT_MemTable table, 243 FT_Pointer block ) 244 { 245 FT_Memory memory = table->memory; 246 247 248 memory->user = table->memory_user; 249 table->free( memory, block ); 250 memory->user = table; 251 } 252 253 254 static void 255 ft_mem_table_resize( FT_MemTable table ) 256 { 257 FT_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 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 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* 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 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 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 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 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 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 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 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 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 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 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