1 /***************************************************************************/ 2 /* */ 3 /* ftdbgmem.c */ 4 /* */ 5 /* Memory debugger (body). */ 6 /* */ 7 /* Copyright 2001-2018 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 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 FT_MemTable ft_mem_table_new(FT_Memory memory)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 ) 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 ft_mem_table_destroy(FT_MemTable table)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 (void*)node->address, 371 node->size, 372 FT_FILENAME( node->source->file_name ), 373 node->source->line_no ); 374 375 leak_count++; 376 leaks += node->size; 377 378 ft_mem_table_free( table, node->address ); 379 } 380 381 node->address = NULL; 382 node->size = 0; 383 384 ft_mem_table_free( table, node ); 385 node = next; 386 } 387 table->buckets[i] = NULL; 388 } 389 390 ft_mem_table_free( table, table->buckets ); 391 table->buckets = NULL; 392 393 table->size = 0; 394 table->nodes = 0; 395 396 /* remove all sources */ 397 for ( i = 0; i < FT_MEM_SOURCE_BUCKETS; i++ ) 398 { 399 FT_MemSource source, next; 400 401 402 for ( source = table->sources[i]; source != NULL; source = next ) 403 { 404 next = source->link; 405 ft_mem_table_free( table, source ); 406 } 407 408 table->sources[i] = NULL; 409 } 410 411 printf( "FreeType: total memory allocations = %ld\n", 412 table->alloc_total ); 413 printf( "FreeType: maximum memory footprint = %ld\n", 414 table->alloc_max ); 415 416 ft_mem_table_free( table, table ); 417 418 if ( leak_count > 0 ) 419 ft_mem_debug_panic( 420 "FreeType: %ld bytes of memory leaked in %ld blocks\n", 421 leaks, leak_count ); 422 423 printf( "FreeType: no memory leaks detected\n" ); 424 } 425 426 427 static FT_MemNode* ft_mem_table_get_nodep(FT_MemTable table,FT_Byte * address)428 ft_mem_table_get_nodep( FT_MemTable table, 429 FT_Byte* address ) 430 { 431 FT_PtrDist hash; 432 FT_MemNode *pnode, node; 433 434 435 hash = FT_MEM_VAL( address ); 436 pnode = table->buckets + ( hash % (FT_PtrDist)table->size ); 437 438 for (;;) 439 { 440 node = pnode[0]; 441 if ( !node ) 442 break; 443 444 if ( node->address == address ) 445 break; 446 447 pnode = &node->link; 448 } 449 return pnode; 450 } 451 452 453 static FT_MemSource ft_mem_table_get_source(FT_MemTable table)454 ft_mem_table_get_source( FT_MemTable table ) 455 { 456 FT_UInt32 hash; 457 FT_MemSource node, *pnode; 458 459 460 /* cast to FT_PtrDist first since void* can be larger */ 461 /* than FT_UInt32 and GCC 4.1.1 emits a warning */ 462 hash = (FT_UInt32)(FT_PtrDist)(void*)_ft_debug_file + 463 (FT_UInt32)( 5 * _ft_debug_lineno ); 464 pnode = &table->sources[hash % FT_MEM_SOURCE_BUCKETS]; 465 466 for (;;) 467 { 468 node = *pnode; 469 if ( !node ) 470 break; 471 472 if ( node->file_name == _ft_debug_file && 473 node->line_no == _ft_debug_lineno ) 474 goto Exit; 475 476 pnode = &node->link; 477 } 478 479 node = (FT_MemSource)ft_mem_table_alloc( table, sizeof ( *node ) ); 480 if ( !node ) 481 ft_mem_debug_panic( 482 "not enough memory to perform memory debugging\n" ); 483 484 node->file_name = _ft_debug_file; 485 node->line_no = _ft_debug_lineno; 486 487 node->cur_blocks = 0; 488 node->max_blocks = 0; 489 node->all_blocks = 0; 490 491 node->cur_size = 0; 492 node->max_size = 0; 493 node->all_size = 0; 494 495 node->cur_max = 0; 496 497 node->link = NULL; 498 node->hash = hash; 499 *pnode = node; 500 501 Exit: 502 return node; 503 } 504 505 506 static void ft_mem_table_set(FT_MemTable table,FT_Byte * address,FT_Long size,FT_Long delta)507 ft_mem_table_set( FT_MemTable table, 508 FT_Byte* address, 509 FT_Long size, 510 FT_Long delta ) 511 { 512 FT_MemNode *pnode, node; 513 514 515 if ( table ) 516 { 517 FT_MemSource source; 518 519 520 pnode = ft_mem_table_get_nodep( table, address ); 521 node = *pnode; 522 if ( node ) 523 { 524 if ( node->size < 0 ) 525 { 526 /* This block was already freed. Our memory is now completely */ 527 /* corrupted! */ 528 /* This can only happen in keep-alive mode. */ 529 ft_mem_debug_panic( 530 "memory heap corrupted (allocating freed block)" ); 531 } 532 else 533 { 534 /* This block was already allocated. This means that our memory */ 535 /* is also corrupted! */ 536 ft_mem_debug_panic( 537 "memory heap corrupted (re-allocating allocated block at" 538 " %p, of size %ld)\n" 539 "org=%s:%d new=%s:%d\n", 540 node->address, node->size, 541 FT_FILENAME( node->source->file_name ), node->source->line_no, 542 FT_FILENAME( _ft_debug_file ), _ft_debug_lineno ); 543 } 544 } 545 546 /* we need to create a new node in this table */ 547 node = (FT_MemNode)ft_mem_table_alloc( table, sizeof ( *node ) ); 548 if ( !node ) 549 ft_mem_debug_panic( "not enough memory to run memory tests" ); 550 551 node->address = address; 552 node->size = size; 553 node->source = source = ft_mem_table_get_source( table ); 554 555 if ( delta == 0 ) 556 { 557 /* this is an allocation */ 558 source->all_blocks++; 559 source->cur_blocks++; 560 if ( source->cur_blocks > source->max_blocks ) 561 source->max_blocks = source->cur_blocks; 562 } 563 564 if ( size > source->cur_max ) 565 source->cur_max = size; 566 567 if ( delta != 0 ) 568 { 569 /* we are growing or shrinking a reallocated block */ 570 source->cur_size += delta; 571 table->alloc_current += delta; 572 } 573 else 574 { 575 /* we are allocating a new block */ 576 source->cur_size += size; 577 table->alloc_current += size; 578 } 579 580 source->all_size += size; 581 582 if ( source->cur_size > source->max_size ) 583 source->max_size = source->cur_size; 584 585 node->free_file_name = NULL; 586 node->free_line_no = 0; 587 588 node->link = pnode[0]; 589 590 pnode[0] = node; 591 table->nodes++; 592 593 table->alloc_total += size; 594 595 if ( table->alloc_current > table->alloc_max ) 596 table->alloc_max = table->alloc_current; 597 598 if ( table->nodes * 3 < table->size || 599 table->size * 3 < table->nodes ) 600 ft_mem_table_resize( table ); 601 } 602 } 603 604 605 static void ft_mem_table_remove(FT_MemTable table,FT_Byte * address,FT_Long delta)606 ft_mem_table_remove( FT_MemTable table, 607 FT_Byte* address, 608 FT_Long delta ) 609 { 610 if ( table ) 611 { 612 FT_MemNode *pnode, node; 613 614 615 pnode = ft_mem_table_get_nodep( table, address ); 616 node = *pnode; 617 if ( node ) 618 { 619 FT_MemSource source; 620 621 622 if ( node->size < 0 ) 623 ft_mem_debug_panic( 624 "freeing memory block at %p more than once at (%s:%ld)\n" 625 "block allocated at (%s:%ld) and released at (%s:%ld)", 626 address, 627 FT_FILENAME( _ft_debug_file ), _ft_debug_lineno, 628 FT_FILENAME( node->source->file_name ), node->source->line_no, 629 FT_FILENAME( node->free_file_name ), node->free_line_no ); 630 631 /* scramble the node's content for additional safety */ 632 FT_MEM_SET( address, 0xF3, node->size ); 633 634 if ( delta == 0 ) 635 { 636 source = node->source; 637 638 source->cur_blocks--; 639 source->cur_size -= node->size; 640 641 table->alloc_current -= node->size; 642 } 643 644 if ( table->keep_alive ) 645 { 646 /* we simply invert the node's size to indicate that the node */ 647 /* was freed. */ 648 node->size = -node->size; 649 node->free_file_name = _ft_debug_file; 650 node->free_line_no = _ft_debug_lineno; 651 } 652 else 653 { 654 table->nodes--; 655 656 *pnode = node->link; 657 658 node->size = 0; 659 node->source = NULL; 660 661 ft_mem_table_free( table, node ); 662 663 if ( table->nodes * 3 < table->size || 664 table->size * 3 < table->nodes ) 665 ft_mem_table_resize( table ); 666 } 667 } 668 else 669 ft_mem_debug_panic( 670 "trying to free unknown block at %p in (%s:%ld)\n", 671 address, 672 FT_FILENAME( _ft_debug_file ), _ft_debug_lineno ); 673 } 674 } 675 676 677 static FT_Pointer ft_mem_debug_alloc(FT_Memory memory,FT_Long size)678 ft_mem_debug_alloc( FT_Memory memory, 679 FT_Long size ) 680 { 681 FT_MemTable table = (FT_MemTable)memory->user; 682 FT_Byte* block; 683 684 685 if ( size <= 0 ) 686 ft_mem_debug_panic( "negative block size allocation (%ld)", size ); 687 688 /* return NULL if the maximum number of allocations was reached */ 689 if ( table->bound_count && 690 table->alloc_count >= table->alloc_count_max ) 691 return NULL; 692 693 /* return NULL if this allocation would overflow the maximum heap size */ 694 if ( table->bound_total && 695 table->alloc_total_max - table->alloc_current > size ) 696 return NULL; 697 698 block = (FT_Byte *)ft_mem_table_alloc( table, size ); 699 if ( block ) 700 { 701 ft_mem_table_set( table, block, size, 0 ); 702 703 table->alloc_count++; 704 } 705 706 _ft_debug_file = "<unknown>"; 707 _ft_debug_lineno = 0; 708 709 return (FT_Pointer)block; 710 } 711 712 713 static void ft_mem_debug_free(FT_Memory memory,FT_Pointer block)714 ft_mem_debug_free( FT_Memory memory, 715 FT_Pointer block ) 716 { 717 FT_MemTable table = (FT_MemTable)memory->user; 718 719 720 if ( !block ) 721 ft_mem_debug_panic( "trying to free NULL in (%s:%ld)", 722 FT_FILENAME( _ft_debug_file ), 723 _ft_debug_lineno ); 724 725 ft_mem_table_remove( table, (FT_Byte*)block, 0 ); 726 727 if ( !table->keep_alive ) 728 ft_mem_table_free( table, block ); 729 730 table->alloc_count--; 731 732 _ft_debug_file = "<unknown>"; 733 _ft_debug_lineno = 0; 734 } 735 736 737 static FT_Pointer ft_mem_debug_realloc(FT_Memory memory,FT_Long cur_size,FT_Long new_size,FT_Pointer block)738 ft_mem_debug_realloc( FT_Memory memory, 739 FT_Long cur_size, 740 FT_Long new_size, 741 FT_Pointer block ) 742 { 743 FT_MemTable table = (FT_MemTable)memory->user; 744 FT_MemNode node, *pnode; 745 FT_Pointer new_block; 746 FT_Long delta; 747 748 const char* file_name = FT_FILENAME( _ft_debug_file ); 749 FT_Long line_no = _ft_debug_lineno; 750 751 752 /* unlikely, but possible */ 753 if ( new_size == cur_size ) 754 return block; 755 756 /* the following is valid according to ANSI C */ 757 #if 0 758 if ( !block || !cur_size ) 759 ft_mem_debug_panic( "trying to reallocate NULL in (%s:%ld)", 760 file_name, line_no ); 761 #endif 762 763 /* while the following is allowed in ANSI C also, we abort since */ 764 /* such case should be handled by FreeType. */ 765 if ( new_size <= 0 ) 766 ft_mem_debug_panic( 767 "trying to reallocate %p to size 0 (current is %ld) in (%s:%ld)", 768 block, cur_size, file_name, line_no ); 769 770 /* check `cur_size' value */ 771 pnode = ft_mem_table_get_nodep( table, (FT_Byte*)block ); 772 node = *pnode; 773 if ( !node ) 774 ft_mem_debug_panic( 775 "trying to reallocate unknown block at %p in (%s:%ld)", 776 block, file_name, line_no ); 777 778 if ( node->size <= 0 ) 779 ft_mem_debug_panic( 780 "trying to reallocate freed block at %p in (%s:%ld)", 781 block, file_name, line_no ); 782 783 if ( node->size != cur_size ) 784 ft_mem_debug_panic( "invalid ft_realloc request for %p. cur_size is " 785 "%ld instead of %ld in (%s:%ld)", 786 block, cur_size, node->size, file_name, line_no ); 787 788 /* return NULL if the maximum number of allocations was reached */ 789 if ( table->bound_count && 790 table->alloc_count >= table->alloc_count_max ) 791 return NULL; 792 793 delta = new_size - cur_size; 794 795 /* return NULL if this allocation would overflow the maximum heap size */ 796 if ( delta > 0 && 797 table->bound_total && 798 table->alloc_current + delta > table->alloc_total_max ) 799 return NULL; 800 801 new_block = (FT_Pointer)ft_mem_table_alloc( table, new_size ); 802 if ( !new_block ) 803 return NULL; 804 805 ft_mem_table_set( table, (FT_Byte*)new_block, new_size, delta ); 806 807 ft_memcpy( new_block, block, cur_size < new_size ? (size_t)cur_size 808 : (size_t)new_size ); 809 810 ft_mem_table_remove( table, (FT_Byte*)block, delta ); 811 812 _ft_debug_file = "<unknown>"; 813 _ft_debug_lineno = 0; 814 815 if ( !table->keep_alive ) 816 ft_mem_table_free( table, block ); 817 818 return new_block; 819 } 820 821 822 extern FT_Int ft_mem_debug_init(FT_Memory memory)823 ft_mem_debug_init( FT_Memory memory ) 824 { 825 FT_MemTable table; 826 FT_Int result = 0; 827 828 829 if ( ft_getenv( "FT2_DEBUG_MEMORY" ) ) 830 { 831 table = ft_mem_table_new( memory ); 832 if ( table ) 833 { 834 const char* p; 835 836 837 memory->user = table; 838 memory->alloc = ft_mem_debug_alloc; 839 memory->realloc = ft_mem_debug_realloc; 840 memory->free = ft_mem_debug_free; 841 842 p = ft_getenv( "FT2_ALLOC_TOTAL_MAX" ); 843 if ( p ) 844 { 845 FT_Long total_max = ft_strtol( p, NULL, 10 ); 846 847 848 if ( total_max > 0 ) 849 { 850 table->bound_total = 1; 851 table->alloc_total_max = total_max; 852 } 853 } 854 855 p = ft_getenv( "FT2_ALLOC_COUNT_MAX" ); 856 if ( p ) 857 { 858 FT_Long total_count = ft_strtol( p, NULL, 10 ); 859 860 861 if ( total_count > 0 ) 862 { 863 table->bound_count = 1; 864 table->alloc_count_max = total_count; 865 } 866 } 867 868 p = ft_getenv( "FT2_KEEP_ALIVE" ); 869 if ( p ) 870 { 871 FT_Long keep_alive = ft_strtol( p, NULL, 10 ); 872 873 874 if ( keep_alive > 0 ) 875 table->keep_alive = 1; 876 } 877 878 result = 1; 879 } 880 } 881 return result; 882 } 883 884 885 extern void ft_mem_debug_done(FT_Memory memory)886 ft_mem_debug_done( FT_Memory memory ) 887 { 888 FT_MemTable table = (FT_MemTable)memory->user; 889 890 891 if ( table ) 892 { 893 memory->free = table->free; 894 memory->realloc = table->realloc; 895 memory->alloc = table->alloc; 896 897 ft_mem_table_destroy( table ); 898 memory->user = NULL; 899 } 900 } 901 902 903 static int ft_mem_source_compare(const void * p1,const void * p2)904 ft_mem_source_compare( const void* p1, 905 const void* p2 ) 906 { 907 FT_MemSource s1 = *(FT_MemSource*)p1; 908 FT_MemSource s2 = *(FT_MemSource*)p2; 909 910 911 if ( s2->max_size > s1->max_size ) 912 return 1; 913 else if ( s2->max_size < s1->max_size ) 914 return -1; 915 else 916 return 0; 917 } 918 919 920 extern void FT_DumpMemory(FT_Memory memory)921 FT_DumpMemory( FT_Memory memory ) 922 { 923 FT_MemTable table = (FT_MemTable)memory->user; 924 925 926 if ( table ) 927 { 928 FT_MemSource* bucket = table->sources; 929 FT_MemSource* limit = bucket + FT_MEM_SOURCE_BUCKETS; 930 FT_MemSource* sources; 931 FT_Int nn, count; 932 const char* fmt; 933 934 935 count = 0; 936 for ( ; bucket < limit; bucket++ ) 937 { 938 FT_MemSource source = *bucket; 939 940 941 for ( ; source; source = source->link ) 942 count++; 943 } 944 945 sources = (FT_MemSource*) 946 ft_mem_table_alloc( 947 table, count * (FT_Long)sizeof ( *sources ) ); 948 949 count = 0; 950 for ( bucket = table->sources; bucket < limit; bucket++ ) 951 { 952 FT_MemSource source = *bucket; 953 954 955 for ( ; source; source = source->link ) 956 sources[count++] = source; 957 } 958 959 ft_qsort( sources, 960 (size_t)count, 961 sizeof ( *sources ), 962 ft_mem_source_compare ); 963 964 printf( "FreeType Memory Dump: " 965 "current=%ld max=%ld total=%ld count=%ld\n", 966 table->alloc_current, table->alloc_max, 967 table->alloc_total, table->alloc_count ); 968 printf( " block block sizes sizes sizes source\n" ); 969 printf( " count high sum highsum max location\n" ); 970 printf( "-------------------------------------------------\n" ); 971 972 fmt = "%6ld %6ld %8ld %8ld %8ld %s:%d\n"; 973 974 for ( nn = 0; nn < count; nn++ ) 975 { 976 FT_MemSource source = sources[nn]; 977 978 979 printf( fmt, 980 source->cur_blocks, source->max_blocks, 981 source->cur_size, source->max_size, source->cur_max, 982 FT_FILENAME( source->file_name ), 983 source->line_no ); 984 } 985 printf( "------------------------------------------------\n" ); 986 987 ft_mem_table_free( table, sources ); 988 } 989 } 990 991 #else /* !FT_DEBUG_MEMORY */ 992 993 /* ANSI C doesn't like empty source files */ 994 typedef int _debug_mem_dummy; 995 996 #endif /* !FT_DEBUG_MEMORY */ 997 998 999 /* END */ 1000