| OLD | NEW |
| (Empty) |
| 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 "../../include/ft2build.h" | |
| 20 #include "../../include/freetype/config/ftconfig.h" | |
| 21 #include "../../include/freetype/internal/ftdebug.h" | |
| 22 #include "../../include/freetype/internal/ftmemory.h" | |
| 23 #include "../../include/freetype/ftsystem.h" | |
| 24 #include "../../include/freetype/fterrors.h" | |
| 25 #include "../../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 "../../include/freetype/config/ftstdlib.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 | |
| 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 | |
| 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_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 | |
| 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 | |
| 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* | |
| 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 | |
| 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 | |
| 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 | |
| 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 | |
| 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 | |
| 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 | |
| 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 | |
| 819 ft_mem_debug_init( FT_Memory memory ) | |
| 820 { | |
| 821 FT_MemTable table; | |
| 822 FT_Int result = 0; | |
| 823 | |
| 824 | |
| 825 if (1)/* XYQ: always enable ( 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 | |
| 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 | |
| 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 | |
| 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 */ | |
| OLD | NEW |