1 //-----------------------------------------------------------------------------
2 // Copyright (c) 2012 GarageGames, LLC
4 // Permission is hereby granted, free of charge, to any person obtaining a copy
5 // of this software and associated documentation files (the "Software"), to
6 // deal in the Software without restriction, including without limitation the
7 // rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
8 // sell copies of the Software, and to permit persons to whom the Software is
9 // furnished to do so, subject to the following conditions:
11 // The above copyright notice and this permission notice shall be included in
12 // all copies or substantial portions of the Software.
14 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
19 // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
21 //-----------------------------------------------------------------------------
23 #include "platform/platformMemory.h"
24 #include "console/dynamicTypes.h"
25 #include "console/engineAPI.h"
26 #include "core/stream/fileStream.h"
27 #include "core/strings/stringFunctions.h"
28 #include "console/console.h"
29 #include "platform/profiler.h"
30 #include "platform/threads/mutex.h"
31 #include "core/module.h"
33 // If profile paths are enabled, disable profiling of the
34 // memory manager as that would cause a cyclic dependency
35 // through the string table's allocation stuff used by the
36 // profiler (talk about a long sentence...)
38 #ifdef TORQUE_ENABLE_PROFILE_PATH
42 # define PROFILE_START( x )
43 # define PROFILE_END()
44 # define PROFILE_SCOPE( x )
47 #ifdef TORQUE_MULTITHREAD
48 void * gMemMutex
= NULL
;
51 //-------------------------------------- Make sure we don't have the define set
56 enum MemConstants
: U32
61 Reallocated
= BIT(3), /// This flag is set if the memory has been allocated, then 'realloc' is called
64 AllocatedGuard
= 0xCEDEFEDE,
65 FreeGuard
= 0x5555FFFF,
66 MaxAllocationAmount
= 0xFFFFFFFF,
67 TreeNodeAllocCount
= 2048,
70 inline U32
flagToBit( Memory::EFlag flag
)
72 using namespace Memory
;
77 case FLAG_Debug
: bit
= DebugFlag
; break;
78 case FLAG_Global
: bit
= GlobalFlag
; break;
79 case FLAG_Static
: bit
= StaticFlag
; break;
89 static U32 MinPageSize
= 8 * 1024 * 1024;
91 #if !defined(TORQUE_SHIPPING) && defined(TORQUE_DEBUG_GUARD)
92 #define LOG_PAGE_ALLOCS
98 //---------------------------------------------------------------------------
103 ConsoleFunctionGroupBegin( Memory
, "Memory manager utility functions.");
107 /// Red/Black Tree Node - used to store queues of free blocks
115 FreeHeader
*queueHead
;
116 FreeHeader
*queueTail
;
122 // doubly linked list of allocated and free blocks -
123 // contiguous in memory.
124 #ifdef TORQUE_DEBUG_GUARD
131 #ifdef TORQUE_DEBUG_GUARD
132 #ifdef TORQUE_ENABLE_PROFILE_PATH
141 struct AllocatedHeader
143 #ifdef TORQUE_DEBUG_GUARD
151 #ifdef TORQUE_DEBUG_GUARD
152 // an allocated header will only have this stuff if TORQUE_DEBUG_GUARD
155 const char *fileName
;
156 #ifdef TORQUE_ENABLE_PROFILE_PATH
157 const char * profilePath
;
171 #ifdef TORQUE_DEBUG_GUARD
179 // since a free header has at least one cache line (16 bytes)
180 // we can tag some more stuff on:
182 FreeHeader
*nextQueue
; // of the same size
183 FreeHeader
*prevQueue
; // doubly linked
184 TreeNode
*treeNode
; // which tree node we're coming off of.
186 #ifdef TORQUE_DEBUG_GUARD
187 #ifdef TORQUE_ENABLE_PROFILE_PATH
197 PageRecord
*prevPage
;
198 Header
*headerList
; // if headerList is NULL, this is a treeNode page
200 U32 unused
[4]; // even out the record to 32 bytes...
201 // so if we're on a 32-byte cache-line comp, the tree nodes
205 PageRecord
*gPageList
= NULL
;
207 TreeNode
*NIL
= &nil
;
208 TreeNode
*gFreeTreeRoot
= &nil
;
209 TreeNode
*gTreeFreeList
= NULL
;
211 U32 gInsertCount
= 0;
212 U32 gRemoveCount
= 0;
213 U32 gBreakAlloc
= 0xFFFFFFFF;
215 char gLogFilename
[256] = "memlog.txt";
216 bool gEnableLogging
= false;
217 bool gNeverLogLeaks
= 0;
218 bool gAlwaysLogLeaks
= 0;
219 U32 gBytesAllocated
= 0;
220 U32 gBlocksAllocated
= 0;
221 U32 gPageBytesAllocated
= 0;
225 PageRecord
* mCurrentPage
;
226 Header
* mCurrentHeader
;
229 HeapIterator( bool allocatedOnly
= true )
230 : mCurrentPage( gPageList
),
231 mAllocatedOnly( allocatedOnly
),
232 mCurrentHeader( NULL
)
236 mCurrentHeader
= mCurrentPage
->headerList
;
237 while( !mCurrentHeader
&& mCurrentPage
)
239 mCurrentPage
= mCurrentPage
->prevPage
;
240 mCurrentHeader
= mCurrentPage
->headerList
;
243 if( mCurrentHeader
&& mAllocatedOnly
&& !( mCurrentHeader
->flags
& Allocated
) )
244 ++ ( *this ); // Advance to first allocated record.
250 return ( mCurrentHeader
!= NULL
);
252 HeapIterator
& operator ++()
256 if( !mCurrentHeader
)
259 mCurrentPage
= mCurrentPage
->prevPage
;
263 mCurrentHeader
= mCurrentPage
->headerList
;
266 mCurrentHeader
= mCurrentHeader
->next
;
268 while( !mCurrentHeader
|| ( mAllocatedOnly
&& !( mCurrentHeader
->flags
& Allocated
) ) );
272 operator Header
*() const
274 return mCurrentHeader
;
276 Header
* operator *() const
278 return mCurrentHeader
;
280 Header
* operator ->() const
282 return mCurrentHeader
;
286 #ifdef TORQUE_DEBUG_GUARD
288 static bool checkGuard(Header
*header
, bool alloc
)
290 U32 guardVal
= alloc
? AllocatedGuard
: FreeGuard
;
291 for(U32 i
= 0; i
< 4; i
++)
292 if(header
->preguard
[i
] != guardVal
|| header
->postguard
[i
] != guardVal
)
294 Platform::debugBreak();
301 #if !defined(TORQUE_DISABLE_MEMORY_MANAGER)
302 static void setGuard(Header
*header
, bool alloc
)
304 U32 guardVal
= alloc
? AllocatedGuard
: FreeGuard
;
305 for(U32 i
= 0; i
< 4; i
++)
306 header
->preguard
[i
] = header
->postguard
[i
] = guardVal
;
308 #endif // !defined(TORQUE_DISABLE_MEMORY_MANAGER)
310 #endif // TORQUE_DEBUG_GUARD
312 static void memoryError()
314 // free all the pages
315 PageRecord
*walk
= gPageList
;
317 PageRecord
*prev
= walk
->prevPage
;
321 AssertFatal(false, "Error allocating memory! Shutting down.");
322 Platform::AlertOK("Torque Memory Error", "Error allocating memory. Shutting down.\n");
323 Platform::forceShutdown(-1);
326 PageRecord
*allocPage(dsize_t pageSize
)
328 pageSize
+= sizeof(PageRecord
);
329 void* base
= dRealMalloc(pageSize
);
333 PageRecord
*rec
= (PageRecord
*) base
;
334 rec
->basePtr
= (void *) (rec
+ 1);
335 rec
->allocSize
= pageSize
;
336 rec
->prevPage
= gPageList
;
338 rec
->headerList
= NULL
;
342 TreeNode
*allocTreeNode()
346 PageRecord
*newPage
= allocPage(TreeNodeAllocCount
* sizeof(TreeNode
));
347 TreeNode
*walk
= (TreeNode
*) newPage
->basePtr
;
349 gTreeFreeList
= walk
;
350 for(i
= 0; i
< TreeNodeAllocCount
- 1; i
++, walk
++)
351 walk
->parent
= walk
+ 1;
354 TreeNode
*ret
= gTreeFreeList
;
355 gTreeFreeList
= ret
->parent
;
359 void freeTreeNode(TreeNode
*tn
)
361 tn
->parent
= gTreeFreeList
;
366 static U32
validateTreeRecurse(TreeNode
*tree
)
370 // check my left tree
371 S32 lcount
, rcount
, nc
= 0;
373 if(tree
->color
== Red
)
375 if(tree
->left
->color
== Red
|| tree
->right
->color
== Red
)
376 Platform::debugBreak();
381 FreeHeader
*walk
= tree
->queueHead
;
383 Platform::debugBreak();
385 FreeHeader
*prev
= NULL
;
388 if(walk
->prevQueue
!= prev
)
389 Platform::debugBreak();
390 if(walk
->treeNode
!= tree
)
391 Platform::debugBreak();
392 if(walk
->size
!= tree
->size
)
393 Platform::debugBreak();
394 if(!walk
->nextQueue
&& walk
!= tree
->queueTail
)
395 Platform::debugBreak();
397 walk
= walk
->nextQueue
;
400 lcount
= validateTreeRecurse(tree
->left
);
401 rcount
= validateTreeRecurse(tree
->right
);
403 Platform::debugBreak();
407 static void validateParentageRecurse(TreeNode
*tree
)
409 if(tree
->left
!= NIL
)
411 if(tree
->left
->parent
!= tree
)
412 Platform::debugBreak();
414 if(tree
->left
->size
> tree
->size
)
415 Platform::debugBreak();
416 validateParentageRecurse(tree
->left
);
418 if(tree
->right
!= NIL
)
420 if(tree
->right
->parent
!= tree
)
421 Platform::debugBreak();
423 if(tree
->right
->size
< tree
->size
)
424 Platform::debugBreak();
425 validateParentageRecurse(tree
->right
);
429 static void validateTree()
431 if(gFreeTreeRoot
== NIL
)
433 validateParentageRecurse(gFreeTreeRoot
);
434 validateTreeRecurse(gFreeTreeRoot
);
439 #ifdef TORQUE_MULTITHREAD
441 gMemMutex
= Mutex::createMutex();
443 Mutex::lockMutex(gMemMutex
);
447 // first validate the free tree:
449 // now validate all blocks:
450 for(PageRecord
*list
= gPageList
; list
; list
= list
->prevPage
)
453 for(Header
*walk
= list
->headerList
; walk
; walk
= walk
->next
)
455 #ifdef TORQUE_DEBUG_GUARD
456 checkGuard(walk
, walk
->flags
& Allocated
);
458 if(walk
->prev
!= prev
)
459 Platform::debugBreak();
461 if(walk
->next
&& ((const char *)(walk
->next
) != (const char *)(walk
) + sizeof(Header
) + walk
->size
))
462 Platform::debugBreak();
466 #ifdef TORQUE_MULTITHREAD
467 Mutex::unlockMutex(gMemMutex
);
471 #if !defined(TORQUE_DISABLE_MEMORY_MANAGER)
472 static void rotateLeft(TreeNode
*hdr
)
474 TreeNode
*temp
= hdr
->right
;
475 hdr
->right
= temp
->left
;
476 if(temp
->left
!= NIL
)
477 temp
->left
->parent
= hdr
;
478 temp
->parent
= hdr
->parent
;
479 if(temp
->parent
== NIL
)
480 gFreeTreeRoot
= temp
;
481 else if(hdr
== hdr
->parent
->left
)
482 hdr
->parent
->left
= temp
;
484 hdr
->parent
->right
= temp
;
490 #if !defined(TORQUE_DISABLE_MEMORY_MANAGER)
491 static void rotateRight(TreeNode
*hdr
)
493 TreeNode
*temp
= hdr
->left
;
494 hdr
->left
= temp
->right
;
495 if(temp
->right
!= NIL
)
496 temp
->right
->parent
= hdr
;
497 temp
->parent
= hdr
->parent
;
498 if(temp
->parent
== NIL
)
499 gFreeTreeRoot
= temp
;
500 else if(hdr
== hdr
->parent
->left
)
501 hdr
->parent
->left
= temp
;
503 hdr
->parent
->right
= temp
;
509 #if !defined(TORQUE_DISABLE_MEMORY_MANAGER)
510 static void treeInsert(FreeHeader
*fhdr
)
512 #ifdef TORQUE_DEBUG_GUARD
513 checkGuard((Header
*) fhdr
, true); // check to see that it's got allocated guards
514 setGuard((Header
*) fhdr
, false);
518 TreeNode
*newParent
= NIL
;
519 TreeNode
*walk
= gFreeTreeRoot
;
523 if(fhdr
->size
< walk
->size
)
525 else if(fhdr
->size
> walk
->size
)
527 else // tag it on the end of the queue...
529 // insert it on this header...
530 walk
->queueTail
->nextQueue
= fhdr
;
531 fhdr
->prevQueue
= walk
->queueTail
;
532 walk
->queueTail
= fhdr
;
533 fhdr
->nextQueue
= NULL
;
534 fhdr
->treeNode
= walk
;
538 TreeNode
*hdr
= allocTreeNode();
539 hdr
->size
= fhdr
->size
;
540 hdr
->queueHead
= hdr
->queueTail
= fhdr
;
541 fhdr
->nextQueue
= fhdr
->prevQueue
= NULL
;
542 fhdr
->treeNode
= hdr
;
547 hdr
->parent
= newParent
;
551 else if(hdr
->size
< newParent
->size
)
552 newParent
->left
= hdr
;
554 newParent
->right
= hdr
;
556 // do red/black rotations
558 while(hdr
!= gFreeTreeRoot
&& (hdr
->parent
->color
== Red
))
560 TreeNode
*parent
= hdr
->parent
;
561 TreeNode
*pparent
= hdr
->parent
->parent
;
563 if(parent
== pparent
->left
)
565 TreeNode
*temp
= pparent
->right
;
566 if(temp
->color
== Red
)
568 parent
->color
= Black
;
570 pparent
->color
= Red
;
575 if(hdr
== parent
->right
)
579 parent
= hdr
->parent
;
580 pparent
= hdr
->parent
->parent
;
582 parent
->color
= Black
;
583 pparent
->color
= Red
;
584 rotateRight(pparent
);
589 TreeNode
*temp
= pparent
->left
;
590 if(temp
->color
== Red
)
592 parent
->color
= Black
;
594 pparent
->color
= Red
;
599 if(hdr
== parent
->left
)
603 parent
= hdr
->parent
;
604 pparent
= hdr
->parent
->parent
;
606 parent
->color
= Black
;
607 pparent
->color
= Red
;
612 gFreeTreeRoot
->color
= Black
;
617 #if !defined(TORQUE_DISABLE_MEMORY_MANAGER)
618 static void treeRemove(FreeHeader
*hdr
)
620 #ifdef TORQUE_DEBUG_GUARD
621 checkGuard((Header
*) hdr
, false);
622 setGuard((Header
*) hdr
, true);
627 FreeHeader
*prev
= hdr
->prevQueue
;
628 FreeHeader
*next
= hdr
->nextQueue
;
631 prev
->nextQueue
= next
;
633 hdr
->treeNode
->queueHead
= next
;
636 next
->prevQueue
= prev
;
638 hdr
->treeNode
->queueTail
= prev
;
643 TreeNode
*z
= hdr
->treeNode
;
648 if(z
->left
== NIL
|| z
->right
== NIL
)
653 while(y
->left
!= NIL
)
661 x
->parent
= y
->parent
;
664 else if(y
== y
->parent
->left
)
667 y
->parent
->right
= x
;
669 U32 yColor
= y
->color
;
672 // copy y's important fields into z (since we're going to free y)
673 if(z
->parent
->left
== z
)
675 else if(z
->parent
->right
== z
)
676 z
->parent
->right
= y
;
682 y
->right
->parent
= y
;
683 y
->parent
= z
->parent
;
695 while(x
!= gFreeTreeRoot
&& x
->color
== Black
)
698 if(x
== x
->parent
->left
)
700 w
= x
->parent
->right
;
704 x
->parent
->color
= Red
;
705 rotateLeft(x
->parent
);
706 w
= x
->parent
->right
;
708 if(w
->left
->color
== Black
&& w
->right
->color
== Black
)
715 if(w
->right
->color
== Black
)
717 w
->left
->color
= Black
;
719 w
= x
->parent
->right
;
721 w
->color
= x
->parent
->color
;
722 x
->parent
->color
= Black
;
723 w
->right
->color
= Black
;
724 rotateLeft(x
->parent
);
734 x
->parent
->color
= Red
;
735 rotateRight(x
->parent
);
738 if(w
->left
->color
== Black
&& w
->right
->color
== Black
)
745 if(w
->left
->color
== Black
)
747 w
->right
->color
= Black
;
751 w
->color
= x
->parent
->color
;
752 x
->parent
->color
= Black
;
753 w
->left
->color
= Black
;
754 rotateRight(x
->parent
);
765 #if !defined(TORQUE_DISABLE_MEMORY_MANAGER)
766 static FreeHeader
*treeFindSmallestGreaterThan(dsize_t size
)
768 TreeNode
*bestMatch
= NIL
;
769 TreeNode
*walk
= gFreeTreeRoot
;
772 if(size
== walk
->size
)
773 return walk
->queueHead
;
774 else if(size
> walk
->size
)
776 else // size < walk->size
784 return bestMatch
->queueHead
;
790 /// Trigger a breakpoint if ptr is not a valid heap pointer or if its memory guards
791 /// have been destroyed (only if TORQUE_DEBUG_GUARD is enabled).
793 /// @note This function does not allow interior pointers!
795 void checkPtr( void* ptr
)
797 for( HeapIterator iter
; iter
.isValid(); ++ iter
)
799 AllocatedHeader
* header
= ( AllocatedHeader
* ) *iter
;
800 if( header
->getUserPtr() == ptr
)
802 #ifdef TORQUE_DEBUG_GUARD
804 if( !checkGuard( *iter
, true ) )
806 dSprintf( buffer
, sizeof( buffer
), "0x%x is a valid heap pointer but has its guards corrupted", ptr
);
807 Platform::outputDebugString( buffer
);
810 //dSprintf( buffer, sizeof( buffer ), "0x%x is a valid heap pointer", ptr );
811 //Platform::outputDebugString( buffer );
818 dSprintf( buffer
, sizeof( buffer
), "0x%x is not a valid heap pointer", ptr
);
819 Platform::outputDebugString( buffer
);
821 Platform::debugBreak();
824 /// Dump info on all memory blocks that are still allocated.
825 /// @note Only works if TORQUE_DISABLE_MEMORY_MANAGER is not defined; otherwise this is a NOP.
827 void ensureAllFreed()
829 #ifndef TORQUE_DISABLE_MEMORY_MANAGER
834 for( HeapIterator iter
; iter
.isValid(); ++ iter
)
836 AllocatedHeader
* header
= ( AllocatedHeader
* ) *iter
;
837 if( !( header
->flags
& GlobalFlag
) )
839 // Note: can't spill profile paths here since they by
840 // now are all invalid (they're on the now freed string table)
842 #ifdef TORQUE_DEBUG_GUARD
843 Platform::outputDebugString( "MEMORY LEAKED: 0x%x %i %s %s:%i = %i (%i)",
844 header
->getUserPtr(),
846 ( header
->flags
& StaticFlag
? "(static)" : "" ),
847 header
->fileName
, header
->line
, header
->realSize
, header
->size
);
849 bytesLeaked
+= header
->size
;
855 Platform::outputDebugString( "NUM LEAKS: %i (%i bytes)", numLeaks
, bytesLeaked
);
868 void logDumpTraverse(MemDumpLog
*sizes
, TreeNode
*header
, U32 depth
)
872 MemDumpLog
*mySize
= sizes
;
873 while(mySize
->size
< header
->size
)
877 for(FreeHeader
*walk
= header
->queueHead
; walk
; walk
= walk
->nextQueue
)
879 mySize
->count
+= cnt
;
880 mySize
->depthTotal
+= depth
* cnt
;
881 mySize
->maxDepth
= depth
> mySize
->maxDepth
? depth
: mySize
->maxDepth
;
882 mySize
->minDepth
= depth
< mySize
->minDepth
? depth
: mySize
->minDepth
;
883 logDumpTraverse(sizes
, header
->left
, depth
+ 1);
884 logDumpTraverse(sizes
, header
->right
, depth
+ 1);
888 DefineConsoleFunction( validateMemory
, void, ( ),,
889 "@brief Used to validate memory space for the game.\n\n"
890 "@ingroup Debugging" )
896 DefineConsoleFunction( freeMemoryDump
, void, ( ),,
897 "@brief Dumps some useful statistics regarding free memory.\n\n"
898 "Dumps an analysis of \'free chunks\' of memory. "
899 "Does not print how much memory is free.\n\n"
900 "@ingroup Debugging" )
903 MemDumpLog memSizes
[20];
905 for(i
= 0; i
< 20; i
++)
907 memSizes
[i
].size
= startSize
<< i
;
908 memSizes
[i
].count
= 0;
909 memSizes
[i
].depthTotal
= 0;
910 memSizes
[i
].maxDepth
= 0;
911 memSizes
[i
].minDepth
= 1000;
913 memSizes
[19].size
= MaxAllocationAmount
;
914 logDumpTraverse(memSizes
, gFreeTreeRoot
, 1);
917 fullMem
.depthTotal
= 0;
918 fullMem
.maxDepth
= 0;
919 fullMem
.minDepth
= 1000;
921 for(i
= 0; i
< 20; i
++)
923 if(memSizes
[i
].count
)
924 Con::printf("Size: %d - Free blocks: %d Max Depth: %d Min Depth: %d Average Depth: %g",
925 memSizes
[i
].size
, memSizes
[i
].count
, memSizes
[i
].maxDepth
, memSizes
[i
].minDepth
,
926 F32(memSizes
[i
].depthTotal
) / F32(memSizes
[i
].count
));
928 fullMem
.count
+= memSizes
[i
].count
;
929 fullMem
.depthTotal
+= memSizes
[i
].depthTotal
;
930 fullMem
.maxDepth
= memSizes
[i
].maxDepth
> fullMem
.maxDepth
? memSizes
[i
].maxDepth
: fullMem
.maxDepth
;
931 fullMem
.minDepth
= memSizes
[i
].minDepth
< fullMem
.minDepth
? memSizes
[i
].minDepth
: fullMem
.minDepth
;
933 Con::printf("Total free blocks: %d Max Depth: %d Min Depth: %d Average Depth: %g",
934 fullMem
.count
, fullMem
.maxDepth
, fullMem
.minDepth
, F32(fullMem
.depthTotal
) / F32(fullMem
.count
));
937 #ifdef TORQUE_DEBUG_GUARD
939 void flagCurrentAllocs( EFlag flag
)
941 #ifdef TORQUE_ENABLE_PROFILE_PATH
942 if (gProfiler
&& !gProfiler
->isEnabled())
944 gProfiler
->enable(true);
946 //gProfiler->dumpToConsole();
950 U32 bit
= flagToBit( flag
);
951 for( HeapIterator iter
; iter
.isValid(); ++ iter
)
955 DefineEngineFunction(flagCurrentAllocs
, void, (),,
956 "@brief Flags all current memory allocations.\n\n"
957 "Flags all current memory allocations for exclusion in subsequent calls to dumpUnflaggedAllocs(). "
958 "Helpful in detecting memory leaks and analyzing memory usage.\n\n"
959 "@ingroup Debugging" )
964 void dumpUnflaggedAllocs(const char *file
, EFlag flag
)
966 countUnflaggedAllocs(file
, NULL
, flag
);
969 S32
countUnflaggedAllocs(const char * filename
, S32
*outUnflaggedRealloc
, EFlag flag
)
971 S32 unflaggedAllocCount
= 0;
972 S32 unflaggedReAllocCount
= 0;
975 bool useFile
= filename
&& *filename
;
977 useFile
= fws
.open(filename
, Torque::FS::File::Write
);
980 U32 bit
= flagToBit( flag
);
983 for (walk
= gPageList
; walk
; walk
= walk
->prevPage
)
985 for(Header
*probe
= walk
->headerList
; probe
; probe
= probe
->next
)
987 if (probe
->flags
& Allocated
)
989 AllocatedHeader
* pah
= (AllocatedHeader
*)probe
;
990 if (!(pah
->flags
& bit
))
992 // If you want to extract further information from an unflagged
993 // memory allocation, do the following:
994 // U8 *foo = (U8 *)pah;
995 // foo += sizeof(Header);
996 // FooObject *obj = (FooObject *)foo;
997 dSprintf(buffer
, 1023, "%s%s\t%d\t%d\t%d\r\n",
998 pah
->flags
& Reallocated
? "[R] " : "",
999 pah
->fileName
!= NULL
? pah
->fileName
: "Undetermined",
1000 pah
->line
, pah
->realSize
, pah
->allocNum
);
1002 if( pah
->flags
& Reallocated
)
1003 unflaggedReAllocCount
++;
1005 unflaggedAllocCount
++;
1009 fws
.write(dStrlen(buffer
), buffer
);
1010 fws
.write(2, "\r\n");
1014 if( pah
->flags
& Reallocated
)
1017 Con::errorf(buffer
);
1020 #ifdef TORQUE_ENABLE_PROFILE_PATH
1021 static char line
[4096];
1022 dSprintf(line
, sizeof(line
), " %s\r\nreal size=%d",
1023 pah
->profilePath
? pah
->profilePath
: "unknown",
1028 fws
.write(dStrlen(line
), line
);
1029 fws
.write(2, "\r\n");
1033 if( pah
->flags
& Reallocated
)
1048 if( outUnflaggedRealloc
!= NULL
)
1049 *outUnflaggedRealloc
= unflaggedReAllocCount
;
1051 return unflaggedAllocCount
;
1054 DefineEngineFunction(dumpUnflaggedAllocs
, void, ( const char* fileName
), ( "" ),
1055 "@brief Dumps all unflagged memory allocations.\n\n"
1056 "Dumps all memory allocations that were made after a call to flagCurrentAllocs(). "
1057 "Helpful when used with flagCurrentAllocs() for detecting memory leaks and analyzing general memory usage.\n\n"
1058 "@param fileName Optional file path and location to dump all memory allocations not flagged by flagCurrentAllocs(). "
1059 "If left blank, data will be dumped to the console.\n\n"
1061 "dumpMemSnapshot(); // dumps info to console\n"
1062 "dumpMemSnapshot( \"C:/Torque/profilerlog1.txt\" ); // dumps info to file\n"
1064 "@note Available in debug builds only. "
1065 "In torqueConfig.h, TORQUE_DISABLE_MEMORY_MANAGER must be undefined to use this function.\n\n"
1066 "@ingroup Debugging" )
1068 dumpUnflaggedAllocs(fileName
);
1071 static void initLog()
1073 static const char* sInitString
= " --- INIT MEMORY LOG (ACTION): (FILE) (LINE) (SIZE) (ALLOCNUMBER) ---\r\n";
1076 fws
.open(gLogFilename
, Torque::FS::File::Write
);
1077 fws
.write(dStrlen(sInitString
), sInitString
);
1081 #if !defined(TORQUE_DISABLE_MEMORY_MANAGER)
1082 static void logAlloc(const AllocatedHeader
* hdr
, S32 memSize
)
1085 fws
.open(gLogFilename
, Torque::FS::File::ReadWrite
);
1086 fws
.setPosition(fws
.getStreamSize());
1089 dSprintf(buffer
, 1023, "alloc: %s %d %d %d\r\n",
1090 hdr
->fileName
!= NULL
? hdr
->fileName
: "Undetermined",
1091 hdr
->line
, memSize
, hdr
->allocNum
);
1092 fws
.write(dStrlen(buffer
), buffer
);
1097 #if !defined(TORQUE_DISABLE_MEMORY_MANAGER)
1098 static void logRealloc(const AllocatedHeader
* hdr
, S32 memSize
)
1101 fws
.open(gLogFilename
, Torque::FS::File::ReadWrite
);
1102 fws
.setPosition(fws
.getStreamSize());
1105 dSprintf(buffer
, 1023, "realloc: %s %d %d %d\r\n",
1106 hdr
->fileName
!= NULL
? hdr
->fileName
: "Undetermined",
1107 hdr
->line
, memSize
, hdr
->allocNum
);
1108 fws
.write(dStrlen(buffer
), buffer
);
1113 #if !defined(TORQUE_DISABLE_MEMORY_MANAGER)
1114 static void logFree(const AllocatedHeader
* hdr
)
1117 fws
.open(gLogFilename
, Torque::FS::File::ReadWrite
);
1118 fws
.setPosition(fws
.getStreamSize());
1121 dSprintf(buffer
, 1023, "free: %s %d %d\r\n",
1122 hdr
->fileName
!= NULL
? hdr
->fileName
: "Undetermined",
1123 hdr
->line
, hdr
->allocNum
);
1124 fws
.write(dStrlen(buffer
), buffer
);
1127 #endif // !defined(TORQUE_DISABLE_MEMORY_MANAGER)
1131 void enableLogging(const char* fileName
)
1133 dStrcpy(gLogFilename
, fileName
);
1134 if (!gEnableLogging
)
1136 gEnableLogging
= true;
1137 #ifdef TORQUE_DEBUG_GUARD
1143 void disableLogging()
1145 gLogFilename
[0] = '\0';
1146 gEnableLogging
= false;
1149 // CodeReview - this is never called so commented out to save warning.
1150 // Do we want to re-enable it? Might be nice to get leak tracking on
1151 // exit...or maybe that is just a problematical feature we shouldn't
1153 //static void shutdown()
1155 //#ifdef TORQUE_MULTITHREAD
1156 // Mutex::destroyMutex(gMemMutex);
1157 // gMemMutex = NULL;
1160 //#ifdef TORQUE_DEBUG_GUARD
1162 // // write out leaks and such
1163 // const U32 maxNumLeaks = 1024;
1164 // U32 numLeaks = 0;
1166 // AllocatedHeader* pLeaks[maxNumLeaks];
1167 // for (PageRecord * walk = gPageList; walk; walk = walk->prevPage)
1168 // for(Header *probe = walk->headerList; probe; probe = probe->next)
1169 // if ((probe->flags & Allocated) && ((AllocatedHeader *)probe)->fileName != NULL)
1170 // pLeaks[numLeaks++] = (AllocatedHeader *) probe;
1172 // if (numLeaks && !gNeverLogLeaks)
1174 // if (gAlwaysLogLeaks || Platform::AlertOKCancel("Memory Status", "Memory leaks detected. Write to memoryLeaks.log?") == true)
1176 // char buffer[1024];
1177 // FileStream logFile;
1178 // logFile.open("memoryLeaks.log", Torque::FS::File::Write);
1180 // for (U32 i = 0; i < numLeaks; i++)
1182 // dSprintf(buffer, 1023, "Leak in %s: %d (%d)\r\n", pLeaks[i]->fileName, pLeaks[i]->line, pLeaks[i]->allocNum);
1183 // logFile.write(dStrlen(buffer), buffer);
1190 // // then free all the memory pages
1191 // for (PageRecord * walk = gPageList; walk; )
1193 // PageRecord *prev = walk->prevPage;
1199 #if !defined(TORQUE_DISABLE_MEMORY_MANAGER)
1200 static Header
*allocMemPage(dsize_t pageSize
)
1202 pageSize
+= sizeof(Header
);
1203 if(pageSize
< MinPageSize
)
1204 pageSize
= MinPageSize
;
1205 PageRecord
*base
= allocPage(pageSize
);
1207 Header
* rec
= (Header
*)base
->basePtr
;
1208 base
->headerList
= rec
;
1210 rec
->size
= pageSize
- sizeof(Header
);
1214 #ifdef TORQUE_DEBUG_GUARD
1215 setGuard(rec
, true);
1218 #ifdef LOG_PAGE_ALLOCS
1219 gPageBytesAllocated
+= pageSize
;
1220 // total bytes allocated so far will be 0 when TORQUE_DEBUG_GUARD is disabled, so convert that into more meaningful string
1221 const U32 StrSize
= 256;
1222 char strBytesAllocated
[StrSize
];
1223 if (gBytesAllocated
> 0)
1224 dSprintf(strBytesAllocated
, sizeof(strBytesAllocated
), "%i", gBytesAllocated
);
1226 dStrncpy(strBytesAllocated
,"unknown - enable TORQUE_DEBUG_GUARD", StrSize
);
1228 #ifndef TORQUE_MULTITHREAD // May deadlock.
1229 // NOTE: This code may be called within Con::_printf, and if that is the case
1230 // this will infinitly recurse. This is the reason for the code in Con::_printf
1231 // that sets Con::active to false. -patw
1232 if (Con::isActive())
1233 Con::errorf("PlatformMemory: allocating new page, total bytes allocated so far: %s (total bytes in all pages=%i)",strBytesAllocated
,gPageBytesAllocated
);
1240 #if !defined(TORQUE_DISABLE_MEMORY_MANAGER)
1241 static void checkUnusedAlloc(FreeHeader
*header
, U32 size
)
1244 if(header
->size
>= size
+ sizeof(Header
) + 16)
1246 U8
*basePtr
= (U8
*) header
;
1247 basePtr
+= sizeof(Header
);
1248 FreeHeader
*newHeader
= (FreeHeader
*) (basePtr
+ size
);
1249 newHeader
->next
= header
->next
;
1250 newHeader
->prev
= (Header
*) header
;
1251 header
->next
= (Header
*) newHeader
;
1253 newHeader
->next
->prev
= (Header
*) newHeader
;
1254 newHeader
->flags
= 0;
1255 newHeader
->size
= header
->size
- size
- sizeof(Header
);
1256 header
->size
= size
;
1257 #ifdef TORQUE_DEBUG_GUARD
1258 setGuard((Header
*) newHeader
, true);
1260 treeInsert(newHeader
);
1265 #if defined(TORQUE_MULTITHREAD) && !defined(TORQUE_DISABLE_MEMORY_MANAGER)
1266 static bool gReentrantGuard
= false;
1269 #if !defined(TORQUE_DISABLE_MEMORY_MANAGER)
1270 static void* alloc(dsize_t size
, bool array
, const char* fileName
, const U32 line
)
1272 AssertFatal(size
< MaxAllocationAmount
, "Memory::alloc - tried to allocate > MaxAllocationAmount!");
1274 #ifdef TORQUE_MULTITHREAD
1275 if(!gMemMutex
&& !gReentrantGuard
)
1277 gReentrantGuard
= true;
1278 gMemMutex
= Mutex::createMutex();
1279 gReentrantGuard
= false;
1282 if(!gReentrantGuard
)
1283 Mutex::lockMutex(gMemMutex
);
1287 AssertFatal(size
< MaxAllocationAmount
, "Size error.");
1291 #ifdef TORQUE_MULTITHREAD
1292 if(!gReentrantGuard
)
1293 Mutex::unlockMutex(gMemMutex
);
1298 #ifndef TORQUE_ENABLE_PROFILE_PATH
1299 // Note: will cause crash if profile path is on
1300 PROFILE_START(MemoryAlloc
);
1303 #ifdef TORQUE_DEBUG_GUARD
1304 // if we're guarding, round up to the nearest DWORD
1305 size
= ((size
+ 3) & ~0x3);
1307 // round up size to nearest 16 byte boundary (cache lines and all...)
1308 size
= ((size
+ 15) & ~0xF);
1311 FreeHeader
*header
= treeFindSmallestGreaterThan(size
);
1315 header
= (FreeHeader
*) allocMemPage(size
);
1317 // ok, see if there's enough room in the block to make another block
1318 // for this to happen it has to have enough room for a header
1319 // and 16 more bytes.
1321 U8
*basePtr
= (U8
*) header
;
1322 basePtr
+= sizeof(Header
);
1324 checkUnusedAlloc(header
, size
);
1326 AllocatedHeader
*retHeader
= (AllocatedHeader
*) header
;
1327 retHeader
->flags
= array
? (Allocated
| Array
) : Allocated
;
1329 #ifdef TORQUE_DEBUG_GUARD
1330 retHeader
->line
= line
;
1331 retHeader
->fileName
= fileName
;
1332 retHeader
->allocNum
= gCurrAlloc
;
1333 retHeader
->realSize
= size
;
1334 #ifdef TORQUE_ENABLE_PROFILE_PATH
1335 retHeader
->profilePath
= gProfiler
? gProfiler
->getProfilePath() : "pre";
1337 gBytesAllocated
+= size
;
1338 gBlocksAllocated
++;
1339 //static U32 skip = 0;
1340 //if ((++skip % 1000) == 0)
1341 // Con::printf("new=%i, newnew=%i, imagenew=%i",gBytesAllocated,gNewNewTotal,gImageAlloc);
1343 logAlloc(retHeader
, size
);
1345 if(gCurrAlloc
== gBreakAlloc
&& gBreakAlloc
!= 0xFFFFFFFF)
1346 Platform::debugBreak();
1348 #ifndef TORQUE_ENABLE_PROFILE_PATH
1354 // fill the block with the fill value. although this is done in free(), that won't fill
1355 // newly allocated MM memory (which hasn't been freed yet). We use a different fill value
1356 // to diffentiate filled freed memory from filled new memory; this may aid debugging.
1357 #ifndef TORQUE_ENABLE_PROFILE_PATH
1358 PROFILE_START(stompMem1
);
1360 dMemset(basePtr
, 0xCF, size
);
1361 #ifndef TORQUE_ENABLE_PROFILE_PATH
1366 if(gCurrAlloc
== gBreakAlloc
&& gBreakAlloc
!= 0xFFFFFFFF)
1367 Platform::debugBreak();
1371 #ifdef TORQUE_MULTITHREAD
1372 if(!gReentrantGuard
)
1373 Mutex::unlockMutex(gMemMutex
);
1380 #if !defined(TORQUE_DISABLE_MEMORY_MANAGER)
1381 static void free(void* mem
, bool array
)
1388 #ifdef TORQUE_MULTITHREAD
1390 gMemMutex
= Mutex::createMutex();
1392 if( mem
!= gMemMutex
)
1393 Mutex::lockMutex(gMemMutex
);
1398 PROFILE_START(MemoryFree
);
1399 AllocatedHeader
*hdr
= ((AllocatedHeader
*)mem
) - 1;
1401 AssertFatal(hdr
->flags
& Allocated
, avar("Not an allocated block!"));
1402 AssertFatal(((bool)((hdr
->flags
& Array
)==Array
))==array
, avar("Array alloc mismatch. "));
1404 gBlocksAllocated
--;
1405 #ifdef TORQUE_DEBUG_GUARD
1406 gBytesAllocated
-= hdr
->realSize
;
1413 // fill the block with the fill value
1416 #ifndef TORQUE_ENABLE_PROFILE_PATH
1417 PROFILE_START(stompMem2
);
1419 dMemset(mem
, 0xCE, hdr
->size
);
1420 #ifndef TORQUE_ENABLE_PROFILE_PATH
1425 // see if we can merge hdr with the block after it.
1427 Header
* next
= hdr
->next
;
1428 if (next
&& next
->flags
== 0)
1430 treeRemove((FreeHeader
*) next
);
1431 hdr
->size
+= next
->size
+ sizeof(Header
);
1432 hdr
->next
= next
->next
;
1434 next
->next
->prev
= (Header
*) hdr
;
1437 // see if we can merge hdr with the block before it.
1438 Header
* prev
= hdr
->prev
;
1440 if (prev
&& prev
->flags
== 0)
1442 treeRemove((FreeHeader
*) prev
);
1443 prev
->size
+= hdr
->size
+ sizeof(Header
);
1444 prev
->next
= hdr
->next
;
1446 hdr
->next
->prev
= prev
;
1448 hdr
= (AllocatedHeader
*) prev
;
1451 // throw this puppy into the tree!
1452 treeInsert((FreeHeader
*) hdr
);
1457 #ifdef TORQUE_MULTITHREAD
1458 Mutex::unlockMutex(gMemMutex
);
1463 #if !defined(TORQUE_DISABLE_MEMORY_MANAGER)
1464 static void* realloc(void* mem
, dsize_t size
, const char* fileName
, const U32 line
)
1472 return alloc(size
, false, fileName
, line
);
1474 #ifdef TORQUE_MULTITHREAD
1476 gMemMutex
= Mutex::createMutex();
1478 Mutex::lockMutex(gMemMutex
);
1481 AllocatedHeader
* hdr
= ((AllocatedHeader
*)mem
) - 1;
1482 #ifdef TORQUE_DEBUG_GUARD
1483 checkGuard( ( Header
* ) hdr
, true );
1486 AssertFatal((hdr
->flags
& Allocated
) == Allocated
, "Bad block flags.");
1488 size
= (size
+ 0xF) & ~0xF;
1490 U32 oldSize
= hdr
->size
;
1493 #ifdef TORQUE_MULTITHREAD
1494 Mutex::unlockMutex(gMemMutex
);
1498 PROFILE_START(MemoryRealloc
);
1500 FreeHeader
*next
= (FreeHeader
*) hdr
->next
;
1502 #ifdef TORQUE_DEBUG_GUARD
1503 // adjust header size and allocated bytes size
1504 hdr
->realSize
+= size
- oldSize
;
1505 gBytesAllocated
+= size
- oldSize
;
1507 logRealloc(hdr
, size
);
1509 // Add reallocated flag, note header changes will not persist if the realloc
1510 // decides tofree, and then perform a fresh allocation for the memory. The flag will
1511 // be manually set again after this takes place, down at the bottom of this fxn.
1512 hdr
->flags
|= Reallocated
;
1515 // A more useful/robust implementation can be accomplished by storing an additional
1516 // AllocatedHeader* in DEBUG_GUARD builds inside the AllocatedHeader structure
1517 // itself to create a sort of reallocation history. This will be, essentially,
1518 // a allocation header stack for each allocation. Each time the memory is reallocated
1519 // it should use dRealMalloc (IMPORTANT!!) to allocate a AllocatedHeader* and chain
1520 // it to the reallocation history chain, and the dump output changed to display
1521 // reallocation history. It is also important to clean up this chain during 'free'
1522 // using dRealFree (Since memory for the chain was allocated via dRealMalloc).
1524 // See patw for details.
1526 if (next
&& !(next
->flags
& Allocated
) && next
->size
+ hdr
->size
+ sizeof(Header
) >= size
)
1528 // we can merge with the next dude.
1530 hdr
->size
+= sizeof(Header
) + next
->size
;
1531 hdr
->next
= next
->next
;
1533 next
->next
->prev
= (Header
*) hdr
;
1535 checkUnusedAlloc((FreeHeader
*) hdr
, size
);
1538 #ifdef TORQUE_MULTITHREAD
1539 Mutex::unlockMutex(gMemMutex
);
1543 else if(size
< oldSize
)
1545 checkUnusedAlloc((FreeHeader
*) hdr
, size
);
1547 #ifdef TORQUE_MULTITHREAD
1548 Mutex::unlockMutex(gMemMutex
);
1552 #ifdef TORQUE_DEBUG_GUARD
1553 // undo above adjustment because we're going though alloc instead
1554 hdr
->realSize
-= size
- oldSize
;
1555 gBytesAllocated
-= size
- oldSize
;
1557 void* ret
= alloc(size
, false, fileName
, line
);
1558 dMemcpy(ret
, mem
, oldSize
);
1562 // Re-enable the 'Reallocated' flag so that this allocation can be ignored by
1563 // a non-strict run of the flag/dumpunflagged.
1564 hdr
= ((AllocatedHeader
*)ret
) - 1;
1565 hdr
->flags
|= Reallocated
;
1567 #ifdef TORQUE_MULTITHREAD
1568 Mutex::unlockMutex(gMemMutex
);
1574 dsize_t
getMemoryUsed()
1579 for (walk
= gPageList
; walk
; walk
= walk
->prevPage
) {
1580 for(Header
*probe
= walk
->headerList
; probe
; probe
= probe
->next
)
1581 if (probe
->flags
& Allocated
) {
1582 size
+= probe
->size
;
1589 #ifdef TORQUE_DEBUG_GUARD
1590 DefineEngineFunction( dumpAlloc
, void, ( S32 allocNum
),,
1591 "@brief Dumps information about the given allocated memory block.\n\n"
1592 "@param allocNum Memory block to dump information about."
1593 "@note Available in debug builds only. "
1594 "In torqueConfig.h, TORQUE_DISABLE_MEMORY_MANAGER must be undefined to use this function.\n\n"
1595 "@ingroup Debugging")
1598 for( walk
= gPageList
; walk
; walk
= walk
->prevPage
)
1599 for( Header
* probe
= walk
->headerList
; probe
; probe
= probe
->next
)
1600 if( probe
->flags
& Allocated
)
1602 AllocatedHeader
* pah
= ( AllocatedHeader
* ) probe
;
1603 if( pah
->allocNum
== allocNum
)
1605 Con::printf( "file: %s\n"
1610 pah
->fileName
!= NULL
? pah
->fileName
: "Undetermined",
1614 pah
->flags
& Reallocated
? "yes" : "no"
1617 // Dump the profile path, if we have one.
1619 #ifdef TORQUE_ENABLE_PROFILE_PATH
1620 if( pah
->profilePath
&& pah
->profilePath
[ 0 ] )
1621 Con::printf( "profilepath: %s", pah
->profilePath
);
1627 DefineEngineFunction( dumpMemSnapshot
, void, ( const char* fileName
),,
1628 "@brief Dumps a snapshot of current memory to a file.\n\n"
1629 "The total memory used will also be output to the console.\n"
1630 "This function will attempt to create the file if it does not already exist.\n"
1631 "@param fileName Name and path of file to save profiling stats to. Must use forward slashes (/)\n"
1633 "dumpMemSnapshot( \"C:/Torque/ProfilerLogs/profilerlog1.txt\" );\n"
1635 "@note Available in debug builds only. "
1636 "In torqueConfig.h, TORQUE_DISABLE_MEMORY_MANAGER must be undefined to use this function.\n\n"
1637 "@ingroup Debugging")
1640 fws
.open(fileName
, Torque::FS::File::Write
);
1641 char buffer
[ 2048 ];
1644 for (walk
= gPageList
; walk
; walk
= walk
->prevPage
) {
1645 for(Header
*probe
= walk
->headerList
; probe
; probe
= probe
->next
)
1646 if (probe
->flags
& Allocated
) {
1647 AllocatedHeader
* pah
= (AllocatedHeader
*)probe
;
1649 dSprintf( buffer
, sizeof( buffer
), "%s%s\t%d\t%d\t%d\r\n",
1650 pah
->flags
& Reallocated
? "[R] " : "",
1651 pah
->fileName
!= NULL
? pah
->fileName
: "Undetermined",
1652 pah
->line
, pah
->realSize
, pah
->allocNum
);
1653 fws
.write(dStrlen(buffer
), buffer
);
1655 // Dump the profile path, if we have one.
1657 #ifdef TORQUE_ENABLE_PROFILE_PATH
1658 if( pah
->profilePath
)
1660 dSprintf( buffer
, sizeof( buffer
), "%s\r\n\r\n", pah
->profilePath
);
1661 fws
.write( dStrlen( buffer
), buffer
);
1667 Con::errorf("total memory used: %d",getMemoryUsed());
1672 dsize_t
getMemoryAllocated()
1677 void getMemoryInfo( void* ptr
, Info
& info
)
1679 #ifndef TORQUE_DISABLE_MEMORY_MANAGER
1681 AllocatedHeader
* header
= ( ( AllocatedHeader
* ) ptr
) - 1;
1683 info
.mAllocSize
= header
->size
;
1684 #ifdef TORQUE_DEBUG_GUARD
1685 info
.mAllocNumber
= header
->allocNum
;
1686 info
.mLineNumber
= header
->line
;
1687 info
.mFileName
= header
->fileName
;
1689 info
.mIsArray
= header
->flags
& Array
;
1690 info
.mIsGlobal
= header
->flags
& GlobalFlag
;
1691 info
.mIsStatic
= header
->flags
& StaticFlag
;
1696 void setBreakAlloc(U32 breakAlloc
)
1698 gBreakAlloc
= breakAlloc
;
1701 ConsoleFunctionGroupEnd( Memory
);
1703 } // namespace Memory
1705 void setMinimumAllocUnit(U32 allocUnit
)
1707 AssertFatal(isPow2(allocUnit
) && allocUnit
> (2 << 20),
1708 "Error, allocunit must be a power of two, and greater than 2 megs");
1710 MinPageSize
= allocUnit
;
1713 //---------------------------------------------------------------------------
1715 //---------------------------------------------------------------------------
1717 #if !defined(TORQUE_DISABLE_MEMORY_MANAGER)
1719 // Manage our own memory, add overloaded memory operators and functions
1721 void* FN_CDECL
operator new(dsize_t size
, const char* fileName
, const U32 line
)
1723 return Memory::alloc(size
, false, fileName
, line
);
1726 void* FN_CDECL
operator new[](dsize_t size
, const char* fileName
, const U32 line
)
1728 return Memory::alloc(size
, true, fileName
, line
);
1731 void* FN_CDECL
operator new(dsize_t size
)
1733 return Memory::alloc(size
, false, NULL
, 0);
1736 void* FN_CDECL
operator new[](dsize_t size
)
1738 return Memory::alloc(size
, true, NULL
, 0);
1741 void FN_CDECL
operator delete(void* mem
)
1743 Memory::free(mem
, false);
1746 void FN_CDECL
operator delete[](void* mem
)
1748 Memory::free(mem
, true);
1751 void* dMalloc_r(dsize_t in_size
, const char* fileName
, const dsize_t line
)
1753 return Memory::alloc(in_size
, false, fileName
, line
);
1756 void dFree(void* in_pFree
)
1758 Memory::free(in_pFree
, false);
1761 void* dRealloc_r(void* in_pResize
, dsize_t in_size
, const char* fileName
, const dsize_t line
)
1763 return Memory::realloc(in_pResize
, in_size
, fileName
, line
);
1766 AFTER_MODULE_INIT( Sim
)
1768 Con::addVariable( "$Memory::numBlocksAllocated", TypeS32
, &Memory::gBlocksAllocated
,
1769 "Total number of memory blocks currently allocated.\n\n"
1770 "@ingroup Debugging" );
1771 Con::addVariable( "$Memory::numBytesAllocated", TypeS32
, &Memory::gBytesAllocated
,
1772 "Total number of bytes currently allocated.\n\n"
1773 "@ingroup Debugging" );
1778 // Don't manage our own memory
1779 void* dMalloc_r(dsize_t in_size
, const char* fileName
, const dsize_t line
)
1781 return malloc(in_size
);
1784 void dFree(void* in_pFree
)
1789 void* dRealloc_r(void* in_pResize
, dsize_t in_size
, const char* fileName
, const dsize_t line
)
1791 return realloc(in_pResize
,in_size
);