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