1 /***************************************************************************/ 2 /* */ 3 /* ftdbgmem.c */ 4 /* */ 5 /* Memory debugger (body). */ 6 /* */ 7 /* Copyright 2001, 2002 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 31 #include <stdio.h> 32 #include <stdlib.h> 33 34 35 typedef struct FT_MemNodeRec_* FT_MemNode; 36 typedef struct FT_MemTableRec_* FT_MemTable; 37 38 #define FT_MEM_VAL( addr ) ((FT_ULong)(FT_Pointer)( addr )) 39 40 typedef struct FT_MemNodeRec_ 41 { 42 FT_Byte* address; 43 FT_Long size; /* < 0 if the block was freed */ 44 45 const char* alloc_file_name; 46 FT_Long alloc_line_no; 47 48 const char* free_file_name; 49 FT_Long free_line_no; 50 51 FT_MemNode link; 52 53 } FT_MemNodeRec; 54 55 56 typedef struct FT_MemTableRec_ 57 { 58 FT_ULong size; 59 FT_ULong nodes; 60 FT_MemNode* buckets; 61 62 FT_ULong alloc_total; 63 FT_ULong alloc_current; 64 FT_ULong alloc_max; 65 66 const char* file_name; 67 FT_Long line_no; 68 69 FT_Memory memory; 70 FT_Pointer memory_user; 71 FT_Alloc_Func alloc; 72 FT_Free_Func free; 73 FT_Realloc_Func realloc; 74 75 } FT_MemTableRec; 76 77 78 #define FT_MEM_SIZE_MIN 7 79 #define FT_MEM_SIZE_MAX 13845163 80 81 #define FT_FILENAME( x ) ((x) ? (x) : "unknown file") 82 83 84 static const FT_UInt ft_mem_primes[] = 85 { 86 7, 87 11, 88 19, 89 37, 90 73, 91 109, 92 163, 93 251, 94 367, 95 557, 96 823, 97 1237, 98 1861, 99 2777, 100 4177, 101 6247, 102 9371, 103 14057, 104 21089, 105 31627, 106 47431, 107 71143, 108 106721, 109 160073, 110 240101, 111 360163, 112 540217, 113 810343, 114 1215497, 115 1823231, 116 2734867, 117 4102283, 118 6153409, 119 9230113, 120 13845163, 121 }; 122 123 124 125 extern void ft_mem_debug_panic(const char * fmt,...)126 ft_mem_debug_panic( const char* fmt, ... ) 127 { 128 va_list ap; 129 130 131 printf( "FreeType.Debug: " ); 132 133 va_start( ap, fmt ); 134 vprintf( fmt, ap ); 135 va_end( ap ); 136 137 printf( "\n" ); 138 exit( EXIT_FAILURE ); 139 } 140 141 142 static FT_ULong ft_mem_closest_prime(FT_ULong num)143 ft_mem_closest_prime( FT_ULong num ) 144 { 145 FT_UInt i; 146 147 148 for ( i = 0; 149 i < sizeof ( ft_mem_primes ) / sizeof ( ft_mem_primes[0] ); i++ ) 150 if ( ft_mem_primes[i] > num ) 151 return ft_mem_primes[i]; 152 153 return FT_MEM_SIZE_MAX; 154 } 155 156 157 static FT_Pointer ft_mem_table_alloc(FT_MemTable table,FT_Long size)158 ft_mem_table_alloc( FT_MemTable table, 159 FT_Long size ) 160 { 161 FT_Memory memory = table->memory; 162 FT_Pointer block; 163 164 165 memory->user = table->memory_user; 166 block = table->alloc( memory, size ); 167 memory->user = table; 168 169 return block; 170 } 171 172 173 static void ft_mem_table_free(FT_MemTable table,FT_Pointer block)174 ft_mem_table_free( FT_MemTable table, 175 FT_Pointer block ) 176 { 177 FT_Memory memory = table->memory; 178 179 180 memory->user = table->memory_user; 181 table->free( memory, block ); 182 memory->user = table; 183 } 184 185 186 static void ft_mem_table_resize(FT_MemTable table)187 ft_mem_table_resize( FT_MemTable table ) 188 { 189 FT_ULong new_size; 190 191 192 new_size = ft_mem_closest_prime( table->nodes ); 193 if ( new_size != table->size ) 194 { 195 FT_MemNode* new_buckets ; 196 FT_ULong i; 197 198 199 new_buckets = (FT_MemNode *) 200 ft_mem_table_alloc( table, 201 new_size * sizeof ( FT_MemNode ) ); 202 if ( new_buckets == NULL ) 203 return; 204 205 FT_MEM_ZERO( new_buckets, sizeof ( FT_MemNode ) * new_size ); 206 207 for ( i = 0; i < table->size; i++ ) 208 { 209 FT_MemNode node, next, *pnode; 210 FT_ULong hash; 211 212 213 node = table->buckets[i]; 214 while ( node ) 215 { 216 next = node->link; 217 hash = FT_MEM_VAL( node->address ) % new_size; 218 pnode = new_buckets + hash; 219 220 node->link = pnode[0]; 221 pnode[0] = node; 222 223 node = next; 224 } 225 } 226 227 if ( table->buckets ) 228 ft_mem_table_free( table, table->buckets ); 229 230 table->buckets = new_buckets; 231 table->size = new_size; 232 } 233 } 234 235 236 static FT_MemTable ft_mem_table_new(FT_Memory memory)237 ft_mem_table_new( FT_Memory memory ) 238 { 239 FT_MemTable table; 240 241 242 table = (FT_MemTable)memory->alloc( memory, sizeof ( *table ) ); 243 if ( table == NULL ) 244 goto Exit; 245 246 FT_MEM_ZERO( table, sizeof ( *table ) ); 247 248 table->size = FT_MEM_SIZE_MIN; 249 table->nodes = 0; 250 251 table->memory = memory; 252 253 table->memory_user = memory->user; 254 255 table->alloc = memory->alloc; 256 table->realloc = memory->realloc; 257 table->free = memory->free; 258 259 table->buckets = (FT_MemNode *) 260 memory->alloc( memory, 261 table->size * sizeof ( FT_MemNode ) ); 262 if ( table->buckets ) 263 FT_MEM_ZERO( table->buckets, sizeof ( FT_MemNode ) * table->size ); 264 else 265 { 266 memory->free( memory, table ); 267 table = NULL; 268 } 269 270 Exit: 271 return table; 272 } 273 274 275 static void ft_mem_table_destroy(FT_MemTable table)276 ft_mem_table_destroy( FT_MemTable table ) 277 { 278 FT_ULong i; 279 280 281 if ( table ) 282 { 283 FT_Long leak_count = 0; 284 FT_ULong leaks = 0; 285 286 287 for ( i = 0; i < table->size; i++ ) 288 { 289 FT_MemNode *pnode = table->buckets + i, next, node = *pnode; 290 291 292 while ( node ) 293 { 294 next = node->link; 295 node->link = 0; 296 297 if ( node->size > 0 ) 298 { 299 printf( 300 "leaked memory block at address %p, size %8ld in (%s:%ld)\n", 301 node->address, node->size, 302 FT_FILENAME( node->alloc_file_name ), 303 node->alloc_line_no ); 304 305 leak_count++; 306 leaks += node->size; 307 308 ft_mem_table_free( table, node->address ); 309 } 310 311 node->address = NULL; 312 node->size = 0; 313 314 free( node ); 315 node = next; 316 } 317 table->buckets[i] = 0; 318 } 319 ft_mem_table_free( table, table->buckets ); 320 table->buckets = NULL; 321 322 table->size = 0; 323 table->nodes = 0; 324 325 printf( 326 "FreeType: total memory allocations = %ld\n", table->alloc_total ); 327 printf( 328 "FreeType: maximum memory footprint = %ld\n", table->alloc_max ); 329 330 free( table ); 331 332 if ( leak_count > 0 ) 333 ft_mem_debug_panic( 334 "FreeType: %ld bytes of memory leaked in %ld blocks\n", 335 leaks, leak_count ); 336 printf( "FreeType: No memory leaks detected!\n" ); 337 } 338 } 339 340 341 static FT_MemNode* ft_mem_table_get_nodep(FT_MemTable table,FT_Byte * address)342 ft_mem_table_get_nodep( FT_MemTable table, 343 FT_Byte* address ) 344 { 345 FT_ULong hash; 346 FT_MemNode *pnode, node; 347 348 349 hash = FT_MEM_VAL( address ); 350 pnode = table->buckets + ( hash % table->size ); 351 352 for (;;) 353 { 354 node = pnode[0]; 355 if ( !node ) 356 break; 357 358 if ( node->address == address ) 359 break; 360 361 pnode = &node->link; 362 } 363 return pnode; 364 } 365 366 367 static void ft_mem_table_set(FT_MemTable table,FT_Byte * address,FT_ULong size)368 ft_mem_table_set( FT_MemTable table, 369 FT_Byte* address, 370 FT_ULong size ) 371 { 372 FT_MemNode *pnode, node; 373 374 375 if ( table ) 376 { 377 pnode = ft_mem_table_get_nodep( table, address ); 378 node = *pnode; 379 if ( node ) 380 { 381 if ( node->size < 0 ) 382 { 383 /* this block was already freed. This means that our memory is */ 384 /* now completely corrupted! */ 385 ft_mem_debug_panic( 386 "memory heap corrupted (allocating freed block)" ); 387 } 388 else 389 { 390 /* this block was already allocated. This means that our memory */ 391 /* is also corrupted! */ 392 ft_mem_debug_panic( 393 "memory heap corrupted (re-allocating allocated block)" ); 394 } 395 } 396 397 /* we need to create a new node in this table */ 398 node = (FT_MemNode)ft_mem_table_alloc( table, sizeof ( *node ) ); 399 if ( node == NULL ) 400 ft_mem_debug_panic( "not enough memory to run memory tests" ); 401 402 node->address = address; 403 node->size = size; 404 405 node->alloc_file_name = table->file_name; 406 node->alloc_line_no = table->line_no; 407 408 node->free_file_name = NULL; 409 node->free_line_no = 0; 410 411 node->link = pnode[0]; 412 413 pnode[0] = node; 414 table->nodes++; 415 416 table->alloc_total += size; 417 table->alloc_current += size; 418 if ( table->alloc_current > table->alloc_max ) 419 table->alloc_max = table->alloc_current; 420 421 if ( table->nodes * 3 < table->size || 422 table->size * 3 < table->nodes ) 423 ft_mem_table_resize( table ); 424 } 425 } 426 427 428 static void ft_mem_table_remove(FT_MemTable table,FT_Byte * address)429 ft_mem_table_remove( FT_MemTable table, 430 FT_Byte* address ) 431 { 432 if ( table ) 433 { 434 FT_MemNode *pnode, node; 435 436 437 pnode = ft_mem_table_get_nodep( table, address ); 438 node = *pnode; 439 if ( node ) 440 { 441 if ( node->size < 0 ) 442 ft_mem_debug_panic( 443 "freeing memory block at %p more than once at (%s:%ld)\n" 444 "block allocated at (%s:%ld) and released at (%s:%ld)", 445 address, 446 FT_FILENAME( table->file_name ), table->line_no, 447 FT_FILENAME( node->alloc_file_name ), node->alloc_line_no, 448 FT_FILENAME( node->free_file_name ), node->free_line_no ); 449 450 /* we simply invert the node's size to indicate that the node */ 451 /* was freed. We also change its contents. */ 452 FT_MEM_SET( address, 0xF3, node->size ); 453 454 table->alloc_current -= node->size; 455 node->size = -node->size; 456 node->free_file_name = table->file_name; 457 node->free_line_no = table->line_no; 458 } 459 else 460 ft_mem_debug_panic( 461 "trying to free unknown block at %p in (%s:%ld)\n", 462 address, 463 FT_FILENAME( table->file_name ), table->line_no ); 464 } 465 } 466 467 468 extern FT_Pointer ft_mem_debug_alloc(FT_Memory memory,FT_Long size)469 ft_mem_debug_alloc( FT_Memory memory, 470 FT_Long size ) 471 { 472 FT_MemTable table = (FT_MemTable)memory->user; 473 FT_Byte* block; 474 475 476 if ( size <= 0 ) 477 ft_mem_debug_panic( "negative block size allocation (%ld)", size ); 478 479 block = (FT_Byte *)ft_mem_table_alloc( table, size ); 480 if ( block ) 481 ft_mem_table_set( table, block, (FT_ULong)size ); 482 483 table->file_name = NULL; 484 table->line_no = 0; 485 486 return (FT_Pointer) block; 487 } 488 489 490 extern void ft_mem_debug_free(FT_Memory memory,FT_Pointer block)491 ft_mem_debug_free( FT_Memory memory, 492 FT_Pointer block ) 493 { 494 FT_MemTable table = (FT_MemTable)memory->user; 495 496 497 if ( block == NULL ) 498 ft_mem_debug_panic( "trying to free NULL in (%s:%ld)", 499 FT_FILENAME( table->file_name ), 500 table->line_no ); 501 502 ft_mem_table_remove( table, (FT_Byte*)block ); 503 504 /* we never really free the block */ 505 table->file_name = NULL; 506 table->line_no = 0; 507 } 508 509 510 extern FT_Pointer ft_mem_debug_realloc(FT_Memory memory,FT_Long cur_size,FT_Long new_size,FT_Pointer block)511 ft_mem_debug_realloc( FT_Memory memory, 512 FT_Long cur_size, 513 FT_Long new_size, 514 FT_Pointer block ) 515 { 516 FT_MemTable table = (FT_MemTable)memory->user; 517 FT_MemNode node, *pnode; 518 FT_Pointer new_block; 519 520 const char* file_name = FT_FILENAME( table->file_name ); 521 FT_Long line_no = table->line_no; 522 523 524 if ( block == NULL || cur_size == 0 ) 525 ft_mem_debug_panic( "trying to reallocate NULL in (%s:%ld)", 526 file_name, line_no ); 527 528 if ( new_size <= 0 ) 529 ft_mem_debug_panic( 530 "trying to reallocate %p to size 0 (current is %ld) in (%s:%ld)", 531 block, cur_size, file_name, line_no ); 532 533 /* check 'cur_size' value */ 534 pnode = ft_mem_table_get_nodep( table, (FT_Byte*)block ); 535 node = *pnode; 536 if ( !node ) 537 ft_mem_debug_panic( 538 "trying to reallocate unknown block at %p in (%s:%ld)", 539 block, file_name, line_no ); 540 541 if ( node->size <= 0 ) 542 ft_mem_debug_panic( 543 "trying to reallocate freed block at %p in (%s:%ld)", 544 block, file_name, line_no ); 545 546 if ( node->size != cur_size ) 547 ft_mem_debug_panic( "invalid ft_realloc request for %p. cur_size is " 548 "%ld instead of %ld in (%s:%ld)", 549 block, cur_size, node->size, file_name, line_no ); 550 551 new_block = ft_mem_debug_alloc( memory, new_size ); 552 if ( new_block == NULL ) 553 return NULL; 554 555 ft_memcpy( new_block, block, cur_size < new_size ? cur_size : new_size ); 556 557 table->file_name = file_name; 558 table->line_no = line_no; 559 560 ft_mem_debug_free( memory, (FT_Byte*)block ); 561 562 return new_block; 563 } 564 565 566 extern FT_Int ft_mem_debug_init(FT_Memory memory)567 ft_mem_debug_init( FT_Memory memory ) 568 { 569 FT_MemTable table; 570 FT_Int result = 0; 571 572 573 if ( getenv( "FT_DEBUG_MEMORY" ) ) 574 { 575 table = ft_mem_table_new( memory ); 576 if ( table ) 577 { 578 memory->user = table; 579 memory->alloc = ft_mem_debug_alloc; 580 memory->realloc = ft_mem_debug_realloc; 581 memory->free = ft_mem_debug_free; 582 result = 1; 583 } 584 } 585 return result; 586 } 587 588 589 extern void ft_mem_debug_done(FT_Memory memory)590 ft_mem_debug_done( FT_Memory memory ) 591 { 592 FT_MemTable table = (FT_MemTable)memory->user; 593 594 595 if ( table ) 596 { 597 memory->free = table->free; 598 memory->realloc = table->realloc; 599 memory->alloc = table->alloc; 600 601 ft_mem_table_destroy( table ); 602 memory->user = NULL; 603 } 604 } 605 606 607 FT_BASE_DEF( FT_Error ) FT_Alloc_Debug(FT_Memory memory,FT_Long size,void ** P,const char * file_name,FT_Long line_no)608 FT_Alloc_Debug( FT_Memory memory, 609 FT_Long size, 610 void* *P, 611 const char* file_name, 612 FT_Long line_no ) 613 { 614 FT_MemTable table = (FT_MemTable)memory->user; 615 616 617 if ( table ) 618 { 619 table->file_name = file_name; 620 table->line_no = line_no; 621 } 622 return FT_Alloc( memory, size, P ); 623 } 624 625 626 FT_BASE_DEF( FT_Error ) FT_Realloc_Debug(FT_Memory memory,FT_Long current,FT_Long size,void ** P,const char * file_name,FT_Long line_no)627 FT_Realloc_Debug( FT_Memory memory, 628 FT_Long current, 629 FT_Long size, 630 void* *P, 631 const char* file_name, 632 FT_Long line_no ) 633 { 634 FT_MemTable table = (FT_MemTable)memory->user; 635 636 637 if ( table ) 638 { 639 table->file_name = file_name; 640 table->line_no = line_no; 641 } 642 return FT_Realloc( memory, current, size, P ); 643 } 644 645 646 FT_BASE_DEF( void ) FT_Free_Debug(FT_Memory memory,FT_Pointer block,const char * file_name,FT_Long line_no)647 FT_Free_Debug( FT_Memory memory, 648 FT_Pointer block, 649 const char* file_name, 650 FT_Long line_no ) 651 { 652 FT_MemTable table = (FT_MemTable)memory->user; 653 654 655 if ( table ) 656 { 657 table->file_name = file_name; 658 table->line_no = line_no; 659 } 660 FT_Free( memory, (void **)block ); 661 } 662 663 664 #else /* !FT_DEBUG_MEMORY */ 665 666 /* ANSI C doesn't like empty source files */ 667 const FT_Byte _debug_mem_dummy = 0; 668 669 #endif /* !FT_DEBUG_MEMORY */ 670 671 672 /* END */ 673