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