platformMemory.cpp
Engine/source/platform/platformMemory.cpp
Classes:
class
class
class
class
class
class
Red/Black Tree Node - used to store queues of free blocks.
Namespaces:
namespace
Public Enumerations
enum
MemConstants { Allocated = BIT(0) Array = BIT(1) DebugFlag = BIT(2) Reallocated = BIT(3) GlobalFlag = BIT(4) StaticFlag = BIT(5) AllocatedGuard = 0xCEDEFEDE FreeGuard = 0x5555FFFF MaxAllocationAmount = 0xFFFFFFFF TreeNodeAllocCount = 2048 }
enum
RedBlackTokens { Red = 0 Black = 1 }
Public Variables
Public Functions
AFTER_MODULE_INIT(Sim )
flagToBit(Memory::EFlag flag)
operator delete(void * mem)
operator delete[](void * mem)
operator new(dsize_t size)
operator new[](dsize_t size)
setMinimumAllocUnit(U32 allocUnit)
Detailed Description
Public Enumerations
MemConstants
Enumerator
- Allocated = BIT(0)
- Array = BIT(1)
- DebugFlag = BIT(2)
- Reallocated = BIT(3)
- GlobalFlag = BIT(4)
This flag is set if the memory has been allocated, then 'realloc' is called.
- StaticFlag = BIT(5)
- AllocatedGuard = 0xCEDEFEDE
- FreeGuard = 0x5555FFFF
- MaxAllocationAmount = 0xFFFFFFFF
- TreeNodeAllocCount = 2048
RedBlackTokens
Enumerator
- Red = 0
- Black = 1
Public Variables
U32 gImageAlloc
U32 gNewNewTotal
U32 MinPageSize
Public Functions
AFTER_MODULE_INIT(Sim )
dFree(void * in_pFree)
dMalloc_r(dsize_t in_size, const char * fileName, const dsize_t line)
dRealloc_r(void * in_pResize, dsize_t in_size, const char * fileName, const dsize_t line)
flagToBit(Memory::EFlag flag)
operator delete(void * mem)
operator delete[](void * mem)
operator new(dsize_t size)
operator new(dsize_t size, const char * fileName, const U32 line)
operator new[](dsize_t size)
operator new[](dsize_t size, const char * fileName, const U32 line)
setMinimumAllocUnit(U32 allocUnit)
1 2//----------------------------------------------------------------------------- 3// Copyright (c) 2012 GarageGames, LLC 4// 5// Permission is hereby granted, free of charge, to any person obtaining a copy 6// of this software and associated documentation files (the "Software"), to 7// deal in the Software without restriction, including without limitation the 8// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 9// sell copies of the Software, and to permit persons to whom the Software is 10// furnished to do so, subject to the following conditions: 11// 12// The above copyright notice and this permission notice shall be included in 13// all copies or substantial portions of the Software. 14// 15// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 20// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 21// IN THE SOFTWARE. 22//----------------------------------------------------------------------------- 23 24#include "platform/platformMemory.h" 25#include "console/dynamicTypes.h" 26#include "console/engineAPI.h" 27#include "core/stream/fileStream.h" 28#include "core/strings/stringFunctions.h" 29#include "console/console.h" 30#include "platform/profiler.h" 31#include "platform/threads/mutex.h" 32#include "core/module.h" 33 34// If profile paths are enabled, disable profiling of the 35// memory manager as that would cause a cyclic dependency 36// through the string table's allocation stuff used by the 37// profiler (talk about a long sentence...) 38 39#ifdef TORQUE_ENABLE_PROFILE_PATH 40# undef PROFILE_START 41# undef PROFILE_END 42# undef PROFILE_SCOPE 43# define PROFILE_START( x ) 44# define PROFILE_END() 45# define PROFILE_SCOPE( x ) 46#endif 47 48#ifdef TORQUE_MULTITHREAD 49void * gMemMutex = NULL; 50#endif 51 52//-------------------------------------- Make sure we don't have the define set 53#ifdef new 54#undef new 55#endif 56 57enum MemConstants : U32 58{ 59 Allocated = BIT(0), 60 Array = BIT(1), 61 DebugFlag = BIT(2), 62 Reallocated = BIT(3), /// This flag is set if the memory has been allocated, then 'realloc' is called 63 GlobalFlag = BIT(4), 64 StaticFlag = BIT(5), 65 AllocatedGuard = 0xCEDEFEDE, 66 FreeGuard = 0x5555FFFF, 67 MaxAllocationAmount = 0xFFFFFFFF, 68 TreeNodeAllocCount = 2048, 69}; 70 71inline U32 flagToBit( Memory::EFlag flag ) 72{ 73 using namespace Memory; 74 75 U32 bit = 0; 76 switch( flag ) 77 { 78 case FLAG_Debug: bit = DebugFlag; break; 79 case FLAG_Global: bit = GlobalFlag; break; 80 case FLAG_Static: bit = StaticFlag; break; 81 } 82 return bit; 83} 84 85enum RedBlackTokens { 86 Red = 0, 87 Black = 1 88}; 89 90static U32 MinPageSize = 8 * 1024 * 1024; 91 92#if !defined(TORQUE_SHIPPING) && defined(TORQUE_DEBUG_GUARD) 93#define LOG_PAGE_ALLOCS 94#endif 95 96U32 gNewNewTotal = 0; 97U32 gImageAlloc = 0; 98 99//--------------------------------------------------------------------------- 100 101namespace Memory 102{ 103 104ConsoleFunctionGroupBegin( Memory, "Memory manager utility functions."); 105 106struct FreeHeader; 107 108/// Red/Black Tree Node - used to store queues of free blocks 109struct TreeNode 110{ 111 U32 size; 112 TreeNode *parent; 113 TreeNode *left; 114 TreeNode *right; 115 U32 color; 116 FreeHeader *queueHead; 117 FreeHeader *queueTail; 118 U32 unused; 119}; 120 121struct Header 122{ 123 // doubly linked list of allocated and free blocks - 124 // contiguous in memory. 125#ifdef TORQUE_DEBUG_GUARD 126 U32 preguard[4]; 127#endif 128 Header *next; 129 Header *prev; 130 dsize_t size; 131 U32 flags; 132#ifdef TORQUE_DEBUG_GUARD 133 #ifdef TORQUE_ENABLE_PROFILE_PATH 134 U32 unused[5]; 135 #else 136 U32 unused[4]; 137 #endif 138 U32 postguard[4]; 139#endif 140}; 141 142struct AllocatedHeader 143{ 144#ifdef TORQUE_DEBUG_GUARD 145 U32 preguard[4]; 146#endif 147 Header *next; 148 Header *prev; 149 dsize_t size; 150 U32 flags; 151 152#ifdef TORQUE_DEBUG_GUARD 153 // an allocated header will only have this stuff if TORQUE_DEBUG_GUARD 154 U32 line; 155 U32 allocNum; 156 const char *fileName; 157 #ifdef TORQUE_ENABLE_PROFILE_PATH 158 const char * profilePath; 159 #endif 160 U32 realSize; 161 U32 postguard[4]; 162#endif 163 164 void* getUserPtr() 165 { 166 return ( this + 1 ); 167 } 168}; 169 170struct FreeHeader 171{ 172#ifdef TORQUE_DEBUG_GUARD 173 U32 preguard[4]; 174#endif 175 Header *next; 176 Header *prev; 177 dsize_t size; 178 U32 flags; 179 180// since a free header has at least one cache line (16 bytes) 181// we can tag some more stuff on: 182 183 FreeHeader *nextQueue; // of the same size 184 FreeHeader *prevQueue; // doubly linked 185 TreeNode *treeNode; // which tree node we're coming off of. 186 U32 guard; 187#ifdef TORQUE_DEBUG_GUARD 188 #ifdef TORQUE_ENABLE_PROFILE_PATH 189 U32 unused; 190 #endif 191 U32 postguard[4]; 192#endif 193}; 194 195struct PageRecord 196{ 197 dsize_t allocSize; 198 PageRecord *prevPage; 199 Header *headerList; // if headerList is NULL, this is a treeNode page 200 void *basePtr; 201 U32 unused[4]; // even out the record to 32 bytes... 202 // so if we're on a 32-byte cache-line comp, the tree nodes 203 // will cache better 204}; 205 206PageRecord *gPageList = NULL; 207TreeNode nil; 208TreeNode *NIL = &nil; 209TreeNode *gFreeTreeRoot = &nil; 210TreeNode *gTreeFreeList = NULL; 211 212U32 gInsertCount = 0; 213U32 gRemoveCount = 0; 214U32 gBreakAlloc = 0xFFFFFFFF; 215U32 gCurrAlloc = 0; 216char gLogFilename[256] = "memlog.txt"; 217bool gEnableLogging = false; 218bool gNeverLogLeaks = 0; 219bool gAlwaysLogLeaks = 0; 220U32 gBytesAllocated = 0; 221U32 gBlocksAllocated = 0; 222U32 gPageBytesAllocated = 0; 223 224struct HeapIterator 225{ 226 PageRecord* mCurrentPage; 227 Header* mCurrentHeader; 228 bool mAllocatedOnly; 229 230 HeapIterator( bool allocatedOnly = true ) 231 : mCurrentPage( gPageList ), 232 mCurrentHeader( NULL ), 233 mAllocatedOnly( allocatedOnly ) 234 { 235 if( mCurrentPage ) 236 { 237 mCurrentHeader = mCurrentPage->headerList; 238 while( !mCurrentHeader && mCurrentPage ) 239 { 240 mCurrentPage = mCurrentPage->prevPage; 241 mCurrentHeader = mCurrentPage->headerList; 242 } 243 244 if( mCurrentHeader && mAllocatedOnly && !( mCurrentHeader->flags & Allocated ) ) 245 ++ ( *this ); // Advance to first allocated record. 246 } 247 } 248 249 bool isValid() const 250 { 251 return ( mCurrentHeader != NULL ); 252 } 253 HeapIterator& operator++() 254 { 255 do 256 { 257 if( !mCurrentHeader ) 258 { 259 if( mCurrentPage ) 260 mCurrentPage = mCurrentPage->prevPage; 261 262 if( !mCurrentPage ) 263 break; 264 mCurrentHeader = mCurrentPage->headerList; 265 } 266 else 267 mCurrentHeader = mCurrentHeader->next; 268 } 269 while( !mCurrentHeader || ( mAllocatedOnly && !( mCurrentHeader->flags & Allocated ) ) ); 270 271 return *this; 272 } 273 operator Header*() const 274 { 275 return mCurrentHeader; 276 } 277 Header* operator*() const 278 { 279 return mCurrentHeader; 280 } 281 Header* operator->() const 282 { 283 return mCurrentHeader; 284 } 285}; 286 287#ifdef TORQUE_DEBUG_GUARD 288 289static bool checkGuard(Header *header, bool alloc) 290{ 291 U32 guardVal = alloc ? AllocatedGuard : FreeGuard; 292 for(U32 i = 0; i < 4; i++) 293 if(header->preguard[i] != guardVal || header->postguard[i] != guardVal) 294 { 295 Platform::debugBreak(); 296 return false; 297 } 298 299 return true; 300} 301 302#if !defined(TORQUE_DISABLE_MEMORY_MANAGER) 303static void setGuard(Header *header, bool alloc) 304{ 305 U32 guardVal = alloc ? AllocatedGuard : FreeGuard; 306 for(U32 i = 0; i < 4; i++) 307 header->preguard[i] = header->postguard[i] = guardVal; 308} 309#endif // !defined(TORQUE_DISABLE_MEMORY_MANAGER) 310 311#endif // TORQUE_DEBUG_GUARD 312 313static void memoryError() 314{ 315 // free all the pages 316 PageRecord *walk = gPageList; 317 while(walk) { 318 PageRecord *prev = walk->prevPage; 319 dRealFree(walk); 320 walk = prev; 321 } 322 AssertFatal(false, "Error allocating memory! Shutting down."); 323 Platform::AlertOK("Torque Memory Error", "Error allocating memory. Shutting down.\n"); 324 Platform::forceShutdown(-1); 325} 326 327PageRecord *allocPage(dsize_t pageSize) 328{ 329 pageSize += sizeof(PageRecord); 330 void* base = dRealMalloc(pageSize); 331 if (base == NULL) 332 memoryError(); 333 334 PageRecord *rec = (PageRecord *) base; 335 rec->basePtr = (void *) (rec + 1); 336 rec->allocSize = pageSize; 337 rec->prevPage = gPageList; 338 gPageList = rec; 339 rec->headerList = NULL; 340 return rec; 341} 342 343TreeNode *allocTreeNode() 344{ 345 if(!gTreeFreeList) 346 { 347 PageRecord *newPage = allocPage(TreeNodeAllocCount * sizeof(TreeNode)); 348 TreeNode *walk = (TreeNode *) newPage->basePtr; 349 U32 i; 350 gTreeFreeList = walk; 351 for(i = 0; i < TreeNodeAllocCount - 1; i++, walk++) 352 walk->parent = walk + 1; 353 walk->parent = NULL; 354 } 355 TreeNode *ret = gTreeFreeList; 356 gTreeFreeList = ret->parent; 357 return ret; 358} 359 360void freeTreeNode(TreeNode *tn) 361{ 362 tn->parent = gTreeFreeList; 363 gTreeFreeList = tn; 364} 365 366 367static U32 validateTreeRecurse(TreeNode *tree) 368{ 369 if(tree == NIL) 370 return 1; 371 // check my left tree 372 S32 lcount, rcount, nc = 0; 373 374 if(tree->color == Red) 375 { 376 if(tree->left->color == Red || tree->right->color == Red) 377 Platform::debugBreak(); 378 } 379 else 380 nc = 1; 381 382 FreeHeader *walk = tree->queueHead; 383 if(!walk) 384 Platform::debugBreak(); 385 386 FreeHeader *prev = NULL; 387 while(walk) 388 { 389 if(walk->prevQueue != prev) 390 Platform::debugBreak(); 391 if(walk->treeNode != tree) 392 Platform::debugBreak(); 393 if(walk->size != tree->size) 394 Platform::debugBreak(); 395 if(!walk->nextQueue && walk != tree->queueTail) 396 Platform::debugBreak(); 397 prev = walk; 398 walk = walk->nextQueue; 399 } 400 401 lcount = validateTreeRecurse(tree->left); 402 rcount = validateTreeRecurse(tree->right); 403 if(lcount != rcount) 404 Platform::debugBreak(); 405 return lcount + nc; 406} 407 408static void validateParentageRecurse(TreeNode *tree) 409{ 410 if(tree->left != NIL) 411 { 412 if(tree->left->parent != tree) 413 Platform::debugBreak(); 414 415 if(tree->left->size > tree->size) 416 Platform::debugBreak(); 417 validateParentageRecurse(tree->left); 418 } 419 if(tree->right != NIL) 420 { 421 if(tree->right->parent != tree) 422 Platform::debugBreak(); 423 424 if(tree->right->size < tree->size) 425 Platform::debugBreak(); 426 validateParentageRecurse(tree->right); 427 } 428} 429 430static void validateTree() 431{ 432 if(gFreeTreeRoot == NIL) 433 return; 434 validateParentageRecurse(gFreeTreeRoot); 435 validateTreeRecurse(gFreeTreeRoot); 436} 437 438void validate() 439{ 440#ifdef TORQUE_MULTITHREAD 441 if(!gMemMutex) 442 gMemMutex = Mutex::createMutex(); 443 444 Mutex::lockMutex(gMemMutex); 445#endif 446 447 448 // first validate the free tree: 449 validateTree(); 450 // now validate all blocks: 451 for(PageRecord *list = gPageList; list; list = list->prevPage) 452 { 453 Header *prev = NULL; 454 for(Header *walk = list->headerList; walk; walk = walk->next) 455 { 456#ifdef TORQUE_DEBUG_GUARD 457 checkGuard(walk, walk->flags & Allocated); 458#endif 459 if(walk->prev != prev) 460 Platform::debugBreak(); 461 prev = walk; 462 if(walk->next && ((const char *)(walk->next) != (const char *)(walk) + sizeof(Header) + walk->size)) 463 Platform::debugBreak(); 464 } 465 } 466 467#ifdef TORQUE_MULTITHREAD 468 Mutex::unlockMutex(gMemMutex); 469#endif 470} 471 472#if !defined(TORQUE_DISABLE_MEMORY_MANAGER) 473static void rotateLeft(TreeNode *hdr) 474{ 475 TreeNode *temp = hdr->right; 476 hdr->right = temp->left; 477 if(temp->left != NIL) 478 temp->left->parent = hdr; 479 temp->parent = hdr->parent; 480 if(temp->parent == NIL) 481 gFreeTreeRoot = temp; 482 else if(hdr == hdr->parent->left) 483 hdr->parent->left = temp; 484 else 485 hdr->parent->right = temp; 486 temp->left = hdr; 487 hdr->parent = temp; 488} 489#endif 490 491#if !defined(TORQUE_DISABLE_MEMORY_MANAGER) 492static void rotateRight(TreeNode *hdr) 493{ 494 TreeNode *temp = hdr->left; 495 hdr->left = temp->right; 496 if(temp->right != NIL) 497 temp->right->parent = hdr; 498 temp->parent = hdr->parent; 499 if(temp->parent == NIL) 500 gFreeTreeRoot = temp; 501 else if(hdr == hdr->parent->left) 502 hdr->parent->left = temp; 503 else 504 hdr->parent->right = temp; 505 temp->right = hdr; 506 hdr->parent = temp; 507} 508#endif 509 510#if !defined(TORQUE_DISABLE_MEMORY_MANAGER) 511static void treeInsert(FreeHeader *fhdr) 512{ 513#ifdef TORQUE_DEBUG_GUARD 514 checkGuard((Header *) fhdr, true); // check to see that it's got allocated guards 515 setGuard((Header *) fhdr, false); 516#endif 517 //gInsertCount++; 518 519 TreeNode *newParent = NIL; 520 TreeNode *walk = gFreeTreeRoot; 521 while(walk != NIL) 522 { 523 newParent = walk; 524 if(fhdr->size < walk->size) 525 walk = walk->left; 526 else if(fhdr->size > walk->size) 527 walk = walk->right; 528 else // tag it on the end of the queue... 529 { 530 // insert it on this header... 531 walk->queueTail->nextQueue = fhdr; 532 fhdr->prevQueue = walk->queueTail; 533 walk->queueTail = fhdr; 534 fhdr->nextQueue = NULL; 535 fhdr->treeNode = walk; 536 return; 537 } 538 } 539 TreeNode *hdr = allocTreeNode(); 540 hdr->size = fhdr->size; 541 hdr->queueHead = hdr->queueTail = fhdr; 542 fhdr->nextQueue = fhdr->prevQueue = NULL; 543 fhdr->treeNode = hdr; 544 545 hdr->left = NIL; 546 hdr->right = NIL; 547 548 hdr->parent = newParent; 549 550 if(newParent == NIL) 551 gFreeTreeRoot = hdr; 552 else if(hdr->size < newParent->size) 553 newParent->left = hdr; 554 else 555 newParent->right = hdr; 556 557 // do red/black rotations 558 hdr->color = Red; 559 while(hdr != gFreeTreeRoot && (hdr->parent->color == Red)) 560 { 561 TreeNode *parent = hdr->parent; 562 TreeNode *pparent = hdr->parent->parent; 563 564 if(parent == pparent->left) 565 { 566 TreeNode *temp = pparent->right; 567 if(temp->color == Red) 568 { 569 parent->color = Black; 570 temp->color = Black; 571 pparent->color = Red; 572 hdr = pparent; 573 } 574 else 575 { 576 if(hdr == parent->right) 577 { 578 hdr = parent; 579 rotateLeft(hdr); 580 parent = hdr->parent; 581 pparent = hdr->parent->parent; 582 } 583 parent->color = Black; 584 pparent->color = Red; 585 rotateRight(pparent); 586 } 587 } 588 else 589 { 590 TreeNode *temp = pparent->left; 591 if(temp->color == Red) 592 { 593 parent->color = Black; 594 temp->color = Black; 595 pparent->color = Red; 596 hdr = pparent; 597 } 598 else 599 { 600 if(hdr == parent->left) 601 { 602 hdr = parent; 603 rotateRight(hdr); 604 parent = hdr->parent; 605 pparent = hdr->parent->parent; 606 } 607 parent->color = Black; 608 pparent->color = Red; 609 rotateLeft(pparent); 610 } 611 } 612 } 613 gFreeTreeRoot->color = Black; 614 //validateTree(); 615} 616#endif 617 618#if !defined(TORQUE_DISABLE_MEMORY_MANAGER) 619static void treeRemove(FreeHeader *hdr) 620{ 621#ifdef TORQUE_DEBUG_GUARD 622 checkGuard((Header *) hdr, false); 623 setGuard((Header *) hdr, true); 624#endif 625 //validateTree(); 626 //gRemoveCount++; 627 628 FreeHeader *prev = hdr->prevQueue; 629 FreeHeader *next = hdr->nextQueue; 630 631 if(prev) 632 prev->nextQueue = next; 633 else 634 hdr->treeNode->queueHead = next; 635 636 if(next) 637 next->prevQueue = prev; 638 else 639 hdr->treeNode->queueTail = prev; 640 641 if(prev || next) 642 return; 643 644 TreeNode *z = hdr->treeNode; 645 646 nil.color = Black; 647 648 TreeNode *y, *x; 649 if(z->left == NIL || z->right == NIL) 650 y = z; 651 else 652 { 653 y = z->right; 654 while(y->left != NIL) 655 y = y->left; 656 } 657 if(y->left != NIL) 658 x = y->left; 659 else 660 x = y->right; 661 662 x->parent = y->parent; 663 if(y->parent == NIL) 664 gFreeTreeRoot = x; 665 else if(y == y->parent->left) 666 y->parent->left = x; 667 else 668 y->parent->right = x; 669 670 U32 yColor = y->color; 671 if(y != z) 672 { 673 // copy y's important fields into z (since we're going to free y) 674 if(z->parent->left == z) 675 z->parent->left = y; 676 else if(z->parent->right == z) 677 z->parent->right = y; 678 y->left = z->left; 679 y->right = z->right; 680 if(y->left != NIL) 681 y->left->parent = y; 682 if(y->right != NIL) 683 y->right->parent = y; 684 y->parent = z->parent; 685 if(z->parent == NIL) 686 gFreeTreeRoot = y; 687 y->color = z->color; 688 if(x->parent == z) 689 x->parent = y; 690 //validateTree(); 691 } 692 freeTreeNode(z); 693 694 if(yColor == Black) 695 { 696 while(x != gFreeTreeRoot && x->color == Black) 697 { 698 TreeNode *w; 699 if(x == x->parent->left) 700 { 701 w = x->parent->right; 702 if(w->color == Red) 703 { 704 w->color = Black; 705 x->parent->color = Red; 706 rotateLeft(x->parent); 707 w = x->parent->right; 708 } 709 if(w->left->color == Black && w->right->color == Black) 710 { 711 w->color = Red; 712 x = x->parent; 713 } 714 else 715 { 716 if(w->right->color == Black) 717 { 718 w->left->color = Black; 719 rotateRight(w); 720 w = x->parent->right; 721 } 722 w->color = x->parent->color; 723 x->parent->color = Black; 724 w->right->color = Black; 725 rotateLeft(x->parent); 726 x = gFreeTreeRoot; 727 } 728 } 729 else 730 { 731 w = x->parent->left; 732 if(w->color == Red) 733 { 734 w->color = Black; 735 x->parent->color = Red; 736 rotateRight(x->parent); 737 w = x->parent->left; 738 } 739 if(w->left->color == Black && w->right->color == Black) 740 { 741 w->color = Red; 742 x = x->parent; 743 } 744 else 745 { 746 if(w->left->color == Black) 747 { 748 w->right->color = Black; 749 rotateLeft(w); 750 w = x->parent->left; 751 } 752 w->color = x->parent->color; 753 x->parent->color = Black; 754 w->left->color = Black; 755 rotateRight(x->parent); 756 x = gFreeTreeRoot; 757 } 758 } 759 } 760 x->color = Black; 761 } 762 //validateTree(); 763} 764#endif 765 766#if !defined(TORQUE_DISABLE_MEMORY_MANAGER) 767static FreeHeader *treeFindSmallestGreaterThan(dsize_t size) 768{ 769 TreeNode *bestMatch = NIL; 770 TreeNode *walk = gFreeTreeRoot; 771 while(walk != NIL) 772 { 773 if(size == walk->size) 774 return walk->queueHead; 775 else if(size > walk->size) 776 walk = walk->right; 777 else // size < walk->size 778 { 779 bestMatch = walk; 780 walk = walk->left; 781 } 782 } 783 //validateTree(); 784 if(bestMatch != NIL) 785 return bestMatch->queueHead; 786 787 return NULL; 788} 789#endif 790 791/// Trigger a breakpoint if ptr is not a valid heap pointer or if its memory guards 792/// have been destroyed (only if TORQUE_DEBUG_GUARD is enabled). 793/// 794/// @note This function does not allow interior pointers! 795 796void checkPtr( void* ptr ) 797{ 798 for( HeapIterator iter; iter.isValid(); ++ iter ) 799 { 800 AllocatedHeader* header = ( AllocatedHeader* ) *iter; 801 if( header->getUserPtr() == ptr ) 802 { 803#ifdef TORQUE_DEBUG_GUARD 804 char buffer[ 1024 ]; 805 if( !checkGuard( *iter, true ) ) 806 { 807 dSprintf( buffer, sizeof( buffer ), "0x%x is a valid heap pointer but has its guards corrupted", ptr ); 808 Platform::outputDebugString( buffer ); 809 return; 810 } 811 //dSprintf( buffer, sizeof( buffer ), "0x%x is a valid heap pointer", ptr ); 812 //Platform::outputDebugString( buffer ); 813#endif 814 return; 815 } 816 } 817 818 char buffer[ 1024 ]; 819 dSprintf( buffer, sizeof( buffer ), "0x%x is not a valid heap pointer", ptr ); 820 Platform::outputDebugString( buffer ); 821 822 Platform::debugBreak(); 823} 824 825/// Dump info on all memory blocks that are still allocated. 826/// @note Only works if TORQUE_DISABLE_MEMORY_MANAGER is not defined; otherwise this is a NOP. 827 828void ensureAllFreed() 829{ 830#ifndef TORQUE_DISABLE_MEMORY_MANAGER 831 832 U32 numLeaks = 0; 833 U32 bytesLeaked = 0; 834 835 for( HeapIterator iter; iter.isValid(); ++ iter ) 836 { 837 AllocatedHeader* header = ( AllocatedHeader* ) *iter; 838 if( !( header->flags & GlobalFlag ) ) 839 { 840 // Note: can't spill profile paths here since they by 841 // now are all invalid (they're on the now freed string table) 842 843#ifdef TORQUE_DEBUG_GUARD 844 Platform::outputDebugString( "MEMORY LEAKED: 0x%x %i %s %s:%i = %i (%i)", 845 header->getUserPtr(), 846 header->allocNum, 847 ( header->flags & StaticFlag ? "(static)" : "" ), 848 header->fileName, header->line, header->realSize, header->size ); 849 numLeaks ++; 850 bytesLeaked += header->size; 851#endif 852 } 853 } 854 855 if( numLeaks ) 856 Platform::outputDebugString( "NUM LEAKS: %i (%i bytes)", numLeaks, bytesLeaked ); 857#endif 858} 859 860struct MemDumpLog 861{ 862 U32 size; 863 U32 count; 864 U32 depthTotal; 865 U32 maxDepth; 866 U32 minDepth; 867}; 868 869void logDumpTraverse(MemDumpLog *sizes, TreeNode *header, U32 depth) 870{ 871 if(header == NIL) 872 return; 873 MemDumpLog *mySize = sizes; 874 while(mySize->size < header->size) 875 mySize++; 876 877 U32 cnt = 0; 878 for(FreeHeader *walk = header->queueHead; walk; walk = walk->nextQueue) 879 cnt++; 880 mySize->count += cnt; 881 mySize->depthTotal += depth * cnt; 882 mySize->maxDepth = depth > mySize->maxDepth ? depth : mySize->maxDepth; 883 mySize->minDepth = depth < mySize->minDepth ? depth : mySize->minDepth; 884 logDumpTraverse(sizes, header->left, depth + 1); 885 logDumpTraverse(sizes, header->right, depth + 1); 886} 887 888#ifdef TORQUE_DEBUG 889DefineEngineFunction( validateMemory, void, ( ),, 890 "@brief Used to validate memory space for the game.\n\n" 891 "@ingroup Debugging" ) 892{ 893 validate(); 894} 895#endif 896 897DefineEngineFunction( freeMemoryDump, void, ( ),, 898 "@brief Dumps some useful statistics regarding free memory.\n\n" 899 "Dumps an analysis of \'free chunks\' of memory. " 900 "Does not print how much memory is free.\n\n" 901 "@ingroup Debugging" ) 902{ 903 U32 startSize = 16; 904 MemDumpLog memSizes[20]; 905 U32 i; 906 for(i = 0; i < 20; i++) 907 { 908 memSizes[i].size = startSize << i; 909 memSizes[i].count = 0; 910 memSizes[i].depthTotal = 0; 911 memSizes[i].maxDepth = 0; 912 memSizes[i].minDepth = 1000; 913 } 914 memSizes[19].size = MaxAllocationAmount; 915 logDumpTraverse(memSizes, gFreeTreeRoot, 1); 916 MemDumpLog fullMem; 917 fullMem.count = 0; 918 fullMem.depthTotal = 0; 919 fullMem.maxDepth = 0; 920 fullMem.minDepth = 1000; 921 922 for(i = 0; i < 20; i++) 923 { 924 if(memSizes[i].count) 925 Con::printf("Size: %d - Free blocks: %d Max Depth: %d Min Depth: %d Average Depth: %g", 926 memSizes[i].size, memSizes[i].count, memSizes[i].maxDepth, memSizes[i].minDepth, 927 F32(memSizes[i].depthTotal) / F32(memSizes[i].count)); 928 929 fullMem.count += memSizes[i].count; 930 fullMem.depthTotal += memSizes[i].depthTotal; 931 fullMem.maxDepth = memSizes[i].maxDepth > fullMem.maxDepth ? memSizes[i].maxDepth : fullMem.maxDepth; 932 fullMem.minDepth = memSizes[i].minDepth < fullMem.minDepth ? memSizes[i].minDepth : fullMem.minDepth; 933 } 934 Con::printf("Total free blocks: %d Max Depth: %d Min Depth: %d Average Depth: %g", 935 fullMem.count, fullMem.maxDepth, fullMem.minDepth, F32(fullMem.depthTotal) / F32(fullMem.count)); 936} 937 938#ifdef TORQUE_DEBUG_GUARD 939 940void flagCurrentAllocs( EFlag flag ) 941{ 942#ifdef TORQUE_ENABLE_PROFILE_PATH 943 if (gProfiler && !gProfiler->isEnabled()) 944 { 945 gProfiler->enable(true); 946 // warm it up 947 //gProfiler->dumpToConsole(); 948 } 949#endif 950 951 U32 bit = flagToBit( flag ); 952 for( HeapIterator iter; iter.isValid(); ++ iter ) 953 iter->flags |= bit; 954} 955 956DefineEngineFunction(flagCurrentAllocs, void, (),, 957 "@brief Flags all current memory allocations.\n\n" 958 "Flags all current memory allocations for exclusion in subsequent calls to dumpUnflaggedAllocs(). " 959 "Helpful in detecting memory leaks and analyzing memory usage.\n\n" 960 "@ingroup Debugging" ) 961{ 962 flagCurrentAllocs(); 963} 964 965void dumpUnflaggedAllocs(const char *file, EFlag flag) 966{ 967 countUnflaggedAllocs(file, NULL, flag); 968} 969 970S32 countUnflaggedAllocs(const char * filename, S32 *outUnflaggedRealloc, EFlag flag) 971{ 972 S32 unflaggedAllocCount = 0; 973 S32 unflaggedReAllocCount = 0; 974 975 FileStream fws; 976 bool useFile = filename && *filename; 977 if (useFile) 978 useFile = fws.open(filename, Torque::FS::File::Write); 979 char buffer[1024]; 980 981 U32 bit = flagToBit( flag ); 982 983 PageRecord* walk; 984 for (walk = gPageList; walk; walk = walk->prevPage) 985 { 986 for(Header *probe = walk->headerList; probe; probe = probe->next) 987 { 988 if (probe->flags & Allocated) 989 { 990 AllocatedHeader* pah = (AllocatedHeader*)probe; 991 if (!(pah->flags & bit)) 992 { 993 // If you want to extract further information from an unflagged 994 // memory allocation, do the following: 995 // U8 *foo = (U8 *)pah; 996 // foo += sizeof(Header); 997 // FooObject *obj = (FooObject *)foo; 998 dSprintf(buffer, 1023, "%s%s\t%d\t%d\t%d\r\n", 999 pah->flags & Reallocated ? "[R] " : "", 1000 pah->fileName != NULL ? pah->fileName : "Undetermined", 1001 pah->line, pah->realSize, pah->allocNum); 1002 1003 if( pah->flags & Reallocated ) 1004 unflaggedReAllocCount++; 1005 else 1006 unflaggedAllocCount++; 1007 1008 if (useFile) 1009 { 1010 fws.write(dStrlen(buffer), buffer); 1011 fws.write(2, "\r\n"); 1012 } 1013 else 1014 { 1015 if( pah->flags & Reallocated ) 1016 Con::warnf(buffer); 1017 else 1018 Con::errorf(buffer); 1019 } 1020 1021#ifdef TORQUE_ENABLE_PROFILE_PATH 1022 static char line[4096]; 1023 dSprintf(line, sizeof(line), " %s\r\nreal size=%d", 1024 pah->profilePath ? pah->profilePath : "unknown", 1025 pah->realSize); 1026 1027 if (useFile) 1028 { 1029 fws.write(dStrlen(line), line); 1030 fws.write(2, "\r\n"); 1031 } 1032 else 1033 { 1034 if( pah->flags & Reallocated ) 1035 Con::warnf(line); 1036 else 1037 Con::errorf(line); 1038 } 1039#endif 1040 1041 } 1042 } 1043 } 1044 } 1045 1046 if (useFile) 1047 fws.close(); 1048 1049 if( outUnflaggedRealloc != NULL ) 1050 *outUnflaggedRealloc = unflaggedReAllocCount; 1051 1052 return unflaggedAllocCount; 1053} 1054 1055DefineEngineFunction(dumpUnflaggedAllocs, void, ( const char* fileName ), ( "" ), 1056 "@brief Dumps all unflagged memory allocations.\n\n" 1057 "Dumps all memory allocations that were made after a call to flagCurrentAllocs(). " 1058 "Helpful when used with flagCurrentAllocs() for detecting memory leaks and analyzing general memory usage.\n\n" 1059 "@param fileName Optional file path and location to dump all memory allocations not flagged by flagCurrentAllocs(). " 1060 "If left blank, data will be dumped to the console.\n\n" 1061 "@tsexample\n" 1062 "dumpMemSnapshot(); // dumps info to console\n" 1063 "dumpMemSnapshot( \"C:/Torque/profilerlog1.txt\" ); // dumps info to file\n" 1064 "@endtsexample\n\n" 1065 "@note Available in debug builds only. " 1066 "In torqueConfig.h, TORQUE_DISABLE_MEMORY_MANAGER must be undefined to use this function.\n\n" 1067 "@ingroup Debugging" ) 1068{ 1069 dumpUnflaggedAllocs(fileName); 1070} 1071 1072static void initLog() 1073{ 1074 static const char* sInitString = " --- INIT MEMORY LOG (ACTION): (FILE) (LINE) (SIZE) (ALLOCNUMBER) ---\r\n"; 1075 1076 FileStream fws; 1077 fws.open(gLogFilename, Torque::FS::File::Write); 1078 fws.write(dStrlen(sInitString), sInitString); 1079 fws.close(); 1080} 1081 1082#if !defined(TORQUE_DISABLE_MEMORY_MANAGER) 1083static void logAlloc(const AllocatedHeader* hdr, S32 memSize) 1084{ 1085 FileStream fws; 1086 fws.open(gLogFilename, Torque::FS::File::ReadWrite); 1087 fws.setPosition(fws.getStreamSize()); 1088 1089 char buffer[1024]; 1090 dSprintf(buffer, 1023, "alloc: %s %d %d %d\r\n", 1091 hdr->fileName != NULL ? hdr->fileName : "Undetermined", 1092 hdr->line, memSize, hdr->allocNum); 1093 fws.write(dStrlen(buffer), buffer); 1094 fws.close(); 1095} 1096#endif 1097 1098#if !defined(TORQUE_DISABLE_MEMORY_MANAGER) 1099static void logRealloc(const AllocatedHeader* hdr, S32 memSize) 1100{ 1101 FileStream fws; 1102 fws.open(gLogFilename, Torque::FS::File::ReadWrite); 1103 fws.setPosition(fws.getStreamSize()); 1104 1105 char buffer[1024]; 1106 dSprintf(buffer, 1023, "realloc: %s %d %d %d\r\n", 1107 hdr->fileName != NULL ? hdr->fileName : "Undetermined", 1108 hdr->line, memSize, hdr->allocNum); 1109 fws.write(dStrlen(buffer), buffer); 1110 fws.close(); 1111} 1112#endif 1113 1114#if !defined(TORQUE_DISABLE_MEMORY_MANAGER) 1115static void logFree(const AllocatedHeader* hdr) 1116{ 1117 FileStream fws; 1118 fws.open(gLogFilename, Torque::FS::File::ReadWrite); 1119 fws.setPosition(fws.getStreamSize()); 1120 1121 char buffer[1024]; 1122 dSprintf(buffer, 1023, "free: %s %d %d\r\n", 1123 hdr->fileName != NULL ? hdr->fileName : "Undetermined", 1124 hdr->line, hdr->allocNum); 1125 fws.write(dStrlen(buffer), buffer); 1126 fws.close(); 1127} 1128#endif // !defined(TORQUE_DISABLE_MEMORY_MANAGER) 1129 1130#endif 1131 1132void enableLogging(const char* fileName) 1133{ 1134 dStrcpy(gLogFilename, fileName, 256); 1135 if (!gEnableLogging) 1136 { 1137 gEnableLogging = true; 1138#ifdef TORQUE_DEBUG_GUARD 1139 initLog(); 1140#endif 1141 } 1142} 1143 1144void disableLogging() 1145{ 1146 gLogFilename[0] = '\0'; 1147 gEnableLogging = false; 1148} 1149 1150// CodeReview - this is never called so commented out to save warning. 1151// Do we want to re-enable it? Might be nice to get leak tracking on 1152// exit...or maybe that is just a problematical feature we shouldn't 1153// worry about. 1154//static void shutdown() 1155//{ 1156//#ifdef TORQUE_MULTITHREAD 1157// Mutex::destroyMutex(gMemMutex); 1158// gMemMutex = NULL; 1159//#endif 1160// 1161//#ifdef TORQUE_DEBUG_GUARD 1162// 1163// // write out leaks and such 1164// const U32 maxNumLeaks = 1024; 1165// U32 numLeaks = 0; 1166// 1167// AllocatedHeader* pLeaks[maxNumLeaks]; 1168// for (PageRecord * walk = gPageList; walk; walk = walk->prevPage) 1169// for(Header *probe = walk->headerList; probe; probe = probe->next) 1170// if ((probe->flags & Allocated) && ((AllocatedHeader *)probe)->fileName != NULL) 1171// pLeaks[numLeaks++] = (AllocatedHeader *) probe; 1172// 1173// if (numLeaks && !gNeverLogLeaks) 1174// { 1175// if (gAlwaysLogLeaks || Platform::AlertOKCancel("Memory Status", "Memory leaks detected. Write to memoryLeaks.log?") == true) 1176// { 1177// char buffer[1024]; 1178// FileStream logFile; 1179// logFile.open("memoryLeaks.log", Torque::FS::File::Write); 1180// 1181// for (U32 i = 0; i < numLeaks; i++) 1182// { 1183// dSprintf(buffer, 1023, "Leak in %s: %d (%d)\r\n", pLeaks[i]->fileName, pLeaks[i]->line, pLeaks[i]->allocNum); 1184// logFile.write(dStrlen(buffer), buffer); 1185// } 1186// logFile.close(); 1187// } 1188// } 1189//#endif 1190// 1191// // then free all the memory pages 1192// for (PageRecord * walk = gPageList; walk; ) 1193// { 1194// PageRecord *prev = walk->prevPage; 1195// dRealFree(walk); 1196// walk = prev; 1197// } 1198//} 1199 1200#if !defined(TORQUE_DISABLE_MEMORY_MANAGER) 1201static Header *allocMemPage(dsize_t pageSize) 1202{ 1203 pageSize += sizeof(Header); 1204 if(pageSize < MinPageSize) 1205 pageSize = MinPageSize; 1206 PageRecord *base = allocPage(pageSize); 1207 1208 Header* rec = (Header*)base->basePtr; 1209 base->headerList = rec; 1210 1211 rec->size = pageSize - sizeof(Header); 1212 rec->next = NULL; 1213 rec->prev = NULL; 1214 rec->flags = 0; 1215#ifdef TORQUE_DEBUG_GUARD 1216 setGuard(rec, true); 1217#endif 1218 1219#ifdef LOG_PAGE_ALLOCS 1220 gPageBytesAllocated += pageSize; 1221 // total bytes allocated so far will be 0 when TORQUE_DEBUG_GUARD is disabled, so convert that into more meaningful string 1222 const U32 StrSize = 256; 1223 char strBytesAllocated[StrSize]; 1224 if (gBytesAllocated > 0) 1225 dSprintf(strBytesAllocated, sizeof(strBytesAllocated), "%i", gBytesAllocated); 1226 else 1227 dStrncpy(strBytesAllocated,"unknown - enable TORQUE_DEBUG_GUARD", StrSize); 1228 1229#ifndef TORQUE_MULTITHREAD // May deadlock. 1230 // NOTE: This code may be called within Con::_printf, and if that is the case 1231 // this will infinitly recurse. This is the reason for the code in Con::_printf 1232 // that sets Con::active to false. -patw 1233 if (Con::isActive()) 1234 Con::errorf("PlatformMemory: allocating new page, total bytes allocated so far: %s (total bytes in all pages=%i)",strBytesAllocated,gPageBytesAllocated); 1235#endif 1236#endif 1237 return rec; 1238} 1239#endif 1240 1241#if !defined(TORQUE_DISABLE_MEMORY_MANAGER) 1242static void checkUnusedAlloc(FreeHeader *header, U32 size) 1243{ 1244 //validate(); 1245 if(header->size >= size + sizeof(Header) + 16) 1246 { 1247 U8 *basePtr = (U8 *) header; 1248 basePtr += sizeof(Header); 1249 FreeHeader *newHeader = (FreeHeader *) (basePtr + size); 1250 newHeader->next = header->next; 1251 newHeader->prev = (Header *) header; 1252 header->next = (Header *) newHeader; 1253 if(newHeader->next) 1254 newHeader->next->prev = (Header *) newHeader; 1255 newHeader->flags = 0; 1256 newHeader->size = header->size - size - sizeof(Header); 1257 header->size = size; 1258#ifdef TORQUE_DEBUG_GUARD 1259 setGuard((Header *) newHeader, true); 1260#endif 1261 treeInsert(newHeader); 1262 } 1263} 1264#endif 1265 1266#if defined(TORQUE_MULTITHREAD) && !defined(TORQUE_DISABLE_MEMORY_MANAGER) 1267static bool gReentrantGuard = false; 1268#endif 1269 1270#if !defined(TORQUE_DISABLE_MEMORY_MANAGER) 1271static void* alloc(dsize_t size, bool array, const char* fileName, const U32 line) 1272{ 1273 AssertFatal(size < MaxAllocationAmount, "Memory::alloc - tried to allocate > <a href="/coding/file/platformmemory_8cpp/#platformmemory_8cpp_1a3ffd6d180bfa999dfbafef07fab77f2ca51e0d57677c7f2ef51f5bd801811d0a8">MaxAllocationAmount</a>!"); 1274 1275#ifdef TORQUE_MULTITHREAD 1276 if(!gMemMutex && !gReentrantGuard) 1277 { 1278 gReentrantGuard = true; 1279 gMemMutex = Mutex::createMutex(); 1280 gReentrantGuard = false; 1281 } 1282 1283 if(!gReentrantGuard) 1284 Mutex::lockMutex(gMemMutex); 1285 1286#endif 1287 1288 AssertFatal(size < MaxAllocationAmount, "Size error."); 1289 //validate(); 1290 if (size == 0) 1291 { 1292#ifdef TORQUE_MULTITHREAD 1293 if(!gReentrantGuard) 1294 Mutex::unlockMutex(gMemMutex); 1295#endif 1296 return NULL; 1297 } 1298 1299#ifndef TORQUE_ENABLE_PROFILE_PATH 1300 // Note: will cause crash if profile path is on 1301 PROFILE_START(MemoryAlloc); 1302#endif 1303 1304#ifdef TORQUE_DEBUG_GUARD 1305 // if we're guarding, round up to the nearest DWORD 1306 size = ((size + 3) & ~0x3); 1307#else 1308 // round up size to nearest 16 byte boundary (cache lines and all...) 1309 size = ((size + 15) & ~0xF); 1310#endif 1311 1312 FreeHeader *header = treeFindSmallestGreaterThan(size); 1313 if(header) 1314 treeRemove(header); 1315 else 1316 header = (FreeHeader *) allocMemPage(size); 1317 1318 // ok, see if there's enough room in the block to make another block 1319 // for this to happen it has to have enough room for a header 1320 // and 16 more bytes. 1321 1322 U8 *basePtr = (U8 *) header; 1323 basePtr += sizeof(Header); 1324 1325 checkUnusedAlloc(header, size); 1326 1327 AllocatedHeader *retHeader = (AllocatedHeader *) header; 1328 retHeader->flags = array ? (Allocated | Array) : Allocated; 1329 1330#ifdef TORQUE_DEBUG_GUARD 1331 retHeader->line = line; 1332 retHeader->fileName = fileName; 1333 retHeader->allocNum = gCurrAlloc; 1334 retHeader->realSize = size; 1335#ifdef TORQUE_ENABLE_PROFILE_PATH 1336 retHeader->profilePath = gProfiler ? gProfiler->getProfilePath() : "pre"; 1337#endif 1338 gBytesAllocated += size; 1339 gBlocksAllocated ++; 1340 //static U32 skip = 0; 1341 //if ((++skip % 1000) == 0) 1342 // Con::printf("new=%i",gBytesAllocated,gNewNewTotal,gImageAlloc); 1343 if (gEnableLogging) 1344 logAlloc(retHeader, size); 1345#endif 1346 if(gCurrAlloc == gBreakAlloc && gBreakAlloc != 0xFFFFFFFF) 1347 Platform::debugBreak(); 1348 gCurrAlloc++; 1349#ifndef TORQUE_ENABLE_PROFILE_PATH 1350 PROFILE_END(); 1351#endif 1352 //validate(); 1353 1354#ifdef TORQUE_DEBUG 1355 // fill the block with the fill value. although this is done in free(), that won't fill 1356 // newly allocated MM memory (which hasn't been freed yet). We use a different fill value 1357 // to diffentiate filled freed memory from filled new memory; this may aid debugging. 1358 #ifndef TORQUE_ENABLE_PROFILE_PATH 1359 PROFILE_START(stompMem1); 1360 #endif 1361 dMemset(basePtr, 0xCF, size); 1362 #ifndef TORQUE_ENABLE_PROFILE_PATH 1363 PROFILE_END(); 1364 #endif 1365#endif 1366 1367 if(gCurrAlloc == gBreakAlloc && gBreakAlloc != 0xFFFFFFFF) 1368 Platform::debugBreak(); 1369 1370 gCurrAlloc++; 1371 1372#ifdef TORQUE_MULTITHREAD 1373 if(!gReentrantGuard) 1374 Mutex::unlockMutex(gMemMutex); 1375#endif 1376 1377 return basePtr; 1378} 1379#endif 1380 1381#if !defined(TORQUE_DISABLE_MEMORY_MANAGER) 1382static void free(void* mem, bool array) 1383{ 1384 // validate(); 1385 1386 if (!mem) 1387 return; 1388 1389#ifdef TORQUE_MULTITHREAD 1390 if(!gMemMutex) 1391 gMemMutex = Mutex::createMutex(); 1392 1393 if( mem != gMemMutex ) 1394 Mutex::lockMutex(gMemMutex); 1395 else 1396 gMemMutex = NULL; 1397#endif 1398 1399 PROFILE_START(MemoryFree); 1400 AllocatedHeader *hdr = ((AllocatedHeader *)mem) - 1; 1401 1402 AssertFatal(hdr->flags & Allocated, avar("Not an allocated block!")); 1403 AssertFatal(((bool)((hdr->flags & Array)==Array))==array, avar("<a href="/coding/file/platformmemory_8cpp/#platformmemory_8cpp_1a3ffd6d180bfa999dfbafef07fab77f2caf8303e03242532eebd970f40ee509689">Array</a> alloc mismatch. ")); 1404 1405 gBlocksAllocated --; 1406#ifdef TORQUE_DEBUG_GUARD 1407 gBytesAllocated -= hdr->realSize; 1408 if (gEnableLogging) 1409 logFree(hdr); 1410#endif 1411 1412 hdr->flags = 0; 1413 1414 // fill the block with the fill value 1415 1416#ifdef TORQUE_DEBUG 1417 #ifndef TORQUE_ENABLE_PROFILE_PATH 1418 PROFILE_START(stompMem2); 1419 #endif 1420 dMemset(mem, 0xCE, hdr->size); 1421 #ifndef TORQUE_ENABLE_PROFILE_PATH 1422 PROFILE_END(); 1423 #endif 1424#endif 1425 1426 // see if we can merge hdr with the block after it. 1427 1428 Header* next = hdr->next; 1429 if (next && next->flags == 0) 1430 { 1431 treeRemove((FreeHeader *) next); 1432 hdr->size += next->size + sizeof(Header); 1433 hdr->next = next->next; 1434 if(next->next) 1435 next->next->prev = (Header *) hdr; 1436 } 1437 1438 // see if we can merge hdr with the block before it. 1439 Header* prev = hdr->prev; 1440 1441 if (prev && prev->flags == 0) 1442 { 1443 treeRemove((FreeHeader *) prev); 1444 prev->size += hdr->size + sizeof(Header); 1445 prev->next = hdr->next; 1446 if (hdr->next) 1447 hdr->next->prev = prev; 1448 1449 hdr = (AllocatedHeader *) prev; 1450 } 1451 1452 // throw this puppy into the tree! 1453 treeInsert((FreeHeader *) hdr); 1454 PROFILE_END(); 1455 1456// validate(); 1457 1458#ifdef TORQUE_MULTITHREAD 1459 Mutex::unlockMutex(gMemMutex); 1460#endif 1461} 1462#endif 1463 1464#if !defined(TORQUE_DISABLE_MEMORY_MANAGER) 1465static void* realloc(void* mem, dsize_t size, const char* fileName, const U32 line) 1466{ 1467 //validate(); 1468 if (!size) { 1469 free(mem, false); 1470 return NULL; 1471 } 1472 if(!mem) 1473 return alloc(size, false, fileName, line); 1474 1475#ifdef TORQUE_MULTITHREAD 1476 if(!gMemMutex) 1477 gMemMutex = Mutex::createMutex(); 1478 1479 Mutex::lockMutex(gMemMutex); 1480#endif 1481 1482 AllocatedHeader* hdr = ((AllocatedHeader *)mem) - 1; 1483#ifdef TORQUE_DEBUG_GUARD 1484 checkGuard( ( Header* ) hdr, true ); 1485#endif 1486 1487 AssertFatal((hdr->flags & Allocated) == Allocated, "Bad block flags."); 1488 1489 size = (size + 0xF) & ~0xF; 1490 1491 U32 oldSize = hdr->size; 1492 if(oldSize == size) 1493 { 1494#ifdef TORQUE_MULTITHREAD 1495 Mutex::unlockMutex(gMemMutex); 1496#endif 1497 return mem; 1498 } 1499 PROFILE_START(MemoryRealloc); 1500 1501 FreeHeader *next = (FreeHeader *) hdr->next; 1502 1503#ifdef TORQUE_DEBUG_GUARD 1504 // adjust header size and allocated bytes size 1505 hdr->realSize += size - oldSize; 1506 gBytesAllocated += size - oldSize; 1507 if (gEnableLogging) 1508 logRealloc(hdr, size); 1509 1510 // Add reallocated flag, note header changes will not persist if the realloc 1511 // decides tofree, and then perform a fresh allocation for the memory. The flag will 1512 // be manually set again after this takes place, down at the bottom of this fxn. 1513 hdr->flags |= Reallocated; 1514 1515 // Note on Above ^ 1516 // A more useful/robust implementation can be accomplished by storing an additional 1517 // AllocatedHeader* in DEBUG_GUARD builds inside the AllocatedHeader structure 1518 // itself to create a sort of reallocation history. This will be, essentially, 1519 // a allocation header stack for each allocation. Each time the memory is reallocated 1520 // it should use dRealMalloc (IMPORTANT!!) to allocate a AllocatedHeader* and chain 1521 // it to the reallocation history chain, and the dump output changed to display 1522 // reallocation history. It is also important to clean up this chain during 'free' 1523 // using dRealFree (Since memory for the chain was allocated via dRealMalloc). 1524 // 1525 // See patw for details. 1526#endif 1527 if (next && !(next->flags & Allocated) && next->size + hdr->size + sizeof(Header) >= size) 1528 { 1529 // we can merge with the next dude. 1530 treeRemove(next); 1531 hdr->size += sizeof(Header) + next->size; 1532 hdr->next = next->next; 1533 if(next->next) 1534 next->next->prev = (Header *) hdr; 1535 1536 checkUnusedAlloc((FreeHeader *) hdr, size); 1537 //validate(); 1538 PROFILE_END(); 1539#ifdef TORQUE_MULTITHREAD 1540 Mutex::unlockMutex(gMemMutex); 1541#endif 1542 return mem; 1543 } 1544 else if(size < oldSize) 1545 { 1546 checkUnusedAlloc((FreeHeader *) hdr, size); 1547 PROFILE_END(); 1548#ifdef TORQUE_MULTITHREAD 1549 Mutex::unlockMutex(gMemMutex); 1550#endif 1551 return mem; 1552 } 1553#ifdef TORQUE_DEBUG_GUARD 1554 // undo above adjustment because we're going though alloc instead 1555 hdr->realSize -= size - oldSize; 1556 gBytesAllocated -= size - oldSize; 1557#endif 1558 void* ret = alloc(size, false, fileName, line); 1559 dMemcpy(ret, mem, oldSize); 1560 free(mem, false); 1561 PROFILE_END(); 1562 1563 // Re-enable the 'Reallocated' flag so that this allocation can be ignored by 1564 // a non-strict run of the flag/dumpunflagged. 1565 hdr = ((AllocatedHeader *)ret) - 1; 1566 hdr->flags |= Reallocated; 1567 1568#ifdef TORQUE_MULTITHREAD 1569 Mutex::unlockMutex(gMemMutex); 1570#endif 1571 return ret; 1572} 1573#endif 1574 1575dsize_t getMemoryUsed() 1576{ 1577 U32 size = 0; 1578 1579 PageRecord* walk; 1580 for (walk = gPageList; walk; walk = walk->prevPage) { 1581 for(Header *probe = walk->headerList; probe; probe = probe->next) 1582 if (probe->flags & Allocated) { 1583 size += probe->size; 1584 } 1585 } 1586 1587 return size; 1588} 1589 1590#ifdef TORQUE_DEBUG_GUARD 1591DefineEngineFunction( dumpAlloc, void, ( S32 allocNum ),, 1592 "@brief Dumps information about the given allocated memory block.\n\n" 1593 "@param allocNum <a href="/coding/namespace/namespacememory/">Memory</a> block <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> dump information about." 1594 "@note Available in debug builds only. " 1595 "In torqueConfig.h, TORQUE_DISABLE_MEMORY_MANAGER must be undefined <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> use this function.\n\n" 1596 "@ingroup Debugging") 1597{ 1598 PageRecord* walk; 1599 for( walk = gPageList; walk; walk = walk->prevPage ) 1600 for( Header* probe = walk->headerList; probe; probe = probe->next ) 1601 if( probe->flags & Allocated ) 1602 { 1603 AllocatedHeader* pah = ( AllocatedHeader* ) probe; 1604 if( pah->allocNum == allocNum ) 1605 { 1606 Con::printf( "<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1a702945180aa732857b380a007a7e2a21">file</a>: %s\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" 1607 "line: %<a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1aa42a9a9cb6e2b93d7f825c395af871bf">i</a>\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" 1608 "<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1ab7d671599a7b25ca99a487fa341bc33a">size</a>: %<a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1aa42a9a9cb6e2b93d7f825c395af871bf">i</a>\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" 1609 "allocNum: %<a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1aa42a9a9cb6e2b93d7f825c395af871bf">i</a>\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" 1610 "reallocated: %s", 1611 pah->fileName != NULL ? pah->fileName : "Undetermined", 1612 pah->line, 1613 pah->realSize, 1614 pah->allocNum, 1615 pah->flags & Reallocated ? "yes" : "no" 1616 ); 1617 1618 // Dump the profile path, if we have one. 1619 1620 #ifdef TORQUE_ENABLE_PROFILE_PATH 1621 if( pah->profilePath && pah->profilePath[ 0 ] ) 1622 Con::printf( "profilepath: %s", pah->profilePath ); 1623 #endif 1624 } 1625 } 1626} 1627 1628DefineEngineFunction( dumpMemSnapshot, void, ( const char* fileName ),, 1629 "@brief Dumps <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> snapshot of current memory <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1a702945180aa732857b380a007a7e2a21">file</a>.\n\n" 1630 "The total memory used will also be output <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> the console.\n" 1631 "This function will attempt <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> create the <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1a702945180aa732857b380a007a7e2a21">file</a> if it does not already exist.\n" 1632 "@param fileName Name and path of <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1a702945180aa732857b380a007a7e2a21">file</a> <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> <a href="/coding/namespace/namespacecubemapsaver/#namespacecubemapsaver_1a2e40c81f39564e68e4c85cba2fada0fc">save</a> profiling stats <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a>. Must use forward slashes (/)\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" 1633 "@tsexample\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" 1634 "dumpMemSnapshot( \"C:/Torque/ProfilerLogs/profilerlog1.txt\" );\n" 1635 "@endtsexample\n\n" 1636 "@note Available in debug builds only. " 1637 "In torqueConfig.h, TORQUE_DISABLE_MEMORY_MANAGER must be undefined to use this function.\n\n" 1638 "@ingroup Debugging") 1639{ 1640 FileStream fws; 1641 fws.open(fileName, Torque::FS::File::Write); 1642 char buffer[ 2048 ]; 1643 1644 PageRecord* walk; 1645 for (walk = gPageList; walk; walk = walk->prevPage) { 1646 for(Header *probe = walk->headerList; probe; probe = probe->next) 1647 if (probe->flags & Allocated) { 1648 AllocatedHeader* pah = (AllocatedHeader*)probe; 1649 1650 dSprintf( buffer, sizeof( buffer ), "%s%s\t%d\t%d\t%d\r\n", 1651 pah->flags & Reallocated ? "[R] " : "", 1652 pah->fileName != NULL ? pah->fileName : "Undetermined", 1653 pah->line, pah->realSize, pah->allocNum); 1654 fws.write(dStrlen(buffer), buffer); 1655 1656 // Dump the profile path, if we have one. 1657 1658 #ifdef TORQUE_ENABLE_PROFILE_PATH 1659 if( pah->profilePath ) 1660 { 1661 dSprintf( buffer, sizeof( buffer ), "%s\r\n\r\n", pah->profilePath ); 1662 fws.write( dStrlen( buffer ), buffer ); 1663 } 1664 #endif 1665 } 1666 } 1667 1668 Con::errorf("total memory used: %d",getMemoryUsed()); 1669 fws.close(); 1670} 1671#endif 1672 1673dsize_t getMemoryAllocated() 1674{ 1675 return 0; 1676} 1677 1678void getMemoryInfo( void* ptr, Info& info ) 1679{ 1680 #ifndef TORQUE_DISABLE_MEMORY_MANAGER 1681 1682 AllocatedHeader* header = ( ( AllocatedHeader* ) ptr ) - 1; 1683 1684 info.mAllocSize = header->size; 1685 #ifdef TORQUE_DEBUG_GUARD 1686 info.mAllocNumber = header->allocNum; 1687 info.mLineNumber = header->line; 1688 info.mFileName = header->fileName; 1689 #endif 1690 info.mIsArray = header->flags & Array; 1691 info.mIsGlobal = header->flags & GlobalFlag; 1692 info.mIsStatic = header->flags & StaticFlag; 1693 1694 #endif 1695} 1696 1697void setBreakAlloc(U32 breakAlloc) 1698{ 1699 gBreakAlloc = breakAlloc; 1700} 1701 1702ConsoleFunctionGroupEnd( Memory ); 1703 1704} // namespace Memory 1705 1706void setMinimumAllocUnit(U32 allocUnit) 1707{ 1708 AssertFatal(isPow2(allocUnit) && allocUnit > (2 << 20), 1709 "Error, allocunit must be a power of two, and greater than 2 megs"); 1710 1711 MinPageSize = allocUnit; 1712} 1713 1714//--------------------------------------------------------------------------- 1715 1716//--------------------------------------------------------------------------- 1717 1718#if !defined(TORQUE_DISABLE_MEMORY_MANAGER) 1719 1720// Manage our own memory, add overloaded memory operators and functions 1721 1722void* FN_CDECL operator new(dsize_t size, const char* fileName, const U32 line) 1723{ 1724 return Memory::alloc(size, false, fileName, line); 1725} 1726 1727void* FN_CDECL operator new[](dsize_t size, const char* fileName, const U32 line) 1728{ 1729 return Memory::alloc(size, true, fileName, line); 1730} 1731 1732void* FN_CDECL operator new(dsize_t size) 1733{ 1734 return Memory::alloc(size, false, NULL, 0); 1735} 1736 1737void* FN_CDECL operator new[](dsize_t size) 1738{ 1739 return Memory::alloc(size, true, NULL, 0); 1740} 1741 1742void FN_CDECL operator delete(void* mem) 1743{ 1744 Memory::free(mem, false); 1745} 1746 1747void FN_CDECL operator delete[](void* mem) 1748{ 1749 Memory::free(mem, true); 1750} 1751 1752void* dMalloc_r(dsize_t in_size, const char* fileName, const dsize_t line) 1753{ 1754 return Memory::alloc(in_size, false, fileName, line); 1755} 1756 1757void dFree(void* in_pFree) 1758{ 1759 Memory::free(in_pFree, false); 1760} 1761 1762void* dRealloc_r(void* in_pResize, dsize_t in_size, const char* fileName, const dsize_t line) 1763{ 1764 return Memory::realloc(in_pResize, in_size, fileName, line); 1765} 1766 1767AFTER_MODULE_INIT( Sim ) 1768{ 1769 Con::addVariable( "$Memory::numBlocksAllocated", TypeS32, &Memory::gBlocksAllocated, 1770 "Total number of memory blocks currently allocated.\n\n" 1771 "@ingroup Debugging" ); 1772 Con::addVariable( "$Memory::numBytesAllocated", TypeS32, &Memory::gBytesAllocated, 1773 "Total number of bytes currently allocated.\n\n" 1774 "@ingroup Debugging" ); 1775} 1776 1777#else 1778 1779// Don't manage our own memory 1780void* dMalloc_r(dsize_t in_size, const char* fileName, const dsize_t line) 1781{ 1782 return malloc(in_size); 1783} 1784 1785void dFree(void* in_pFree) 1786{ 1787 free(in_pFree); 1788} 1789 1790void* dRealloc_r(void* in_pResize, dsize_t in_size, const char* fileName, const dsize_t line) 1791{ 1792 return realloc(in_pResize,in_size); 1793} 1794 1795#endif 1796