2 // Copyright (C) 2008 by Martin Moracek
4 // This program is free software; you can redistribute it and/or modify
5 // it under the terms of the GNU General Public License as published by
6 // the Free Software Foundation; either version 2 of the License, or
7 // (at your option) any later version.
9 // This program is distributed in the hope that it will be useful,
10 // but WITHOUT ANY WARRANTY; without even the implied warranty of
11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 // GNU General Public License for more details.
14 // You should have received a copy of the GNU General Public License
15 // along with this program; if not, write to the Free Software
16 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19 // ----------------------------------------------------------------------------
20 // Originally created on 12/22/2000 by Paul Nettle
22 // Copyright 2000, Fluid Studios, Inc., all rights reserved.
24 // For more information, visit HTTP://www.FluidStudios.com
25 // ----------------------------------------------------------------------------
30 * Contains implementation of memory management.
40 #include "vfs/logfile.h"
43 #include "assertion.h"
49 #include "memory/mmgr.h"
53 // May be used for debuggin purposes in the future.
55 // #define TEST_MEMORY_MANAGER
57 // Enable this sucker if you really want to stress-test your app's
58 // memory usage, or to help find hard-to-find bugs
60 // #define STRESS_TEST
62 // Enable this sucker if you want to stress-test your app's error-handling.
63 // Set RANDOM_FAIL to the percentage of failures you want to test
64 // with (0 = none, >100 = all failures).
66 // #define RANDOM_FAILURE 10.0
76 void * reportedAddress
;
80 ::tre::AllocType allocType
;
90 * Locals -- modify these flags to suit your needs
93 const uint hashBits
= 12;
94 bool randomWipe
= true;
95 bool alwaysValidateAll
= true;
96 bool alwaysLogAll
= true;
97 bool alwaysWipeAll
= true;
98 bool cleanupLogOnFirstRun
= true;
99 const uint paddingSize
= 1024; // An extra 8K per allocation!
100 #else /* STRESS_TEST */
101 const uint hashBits
= 12;
102 bool randomWipe
= false;
103 bool alwaysValidateAll
= false;
104 bool alwaysLogAll
= false;
105 bool alwaysWipeAll
= true;
106 bool cleanupLogOnFirstRun
= true;
107 const uint paddingSize
= 4;
108 #endif /* STRESS_TEST */
111 * These values will be used to fill unused and deallocated RAM,
113 uint32 prefixPattern
= 0xbaadf00d;
114 uint32 postfixPattern
= 0xdeadc0de;
115 uint32 unusedPattern
= 0xfeedface;
116 uint32 releasedPattern
= 0xdeadbeef;
121 const unsigned hashSize
= 1 << hashBits
;
122 const char * allocationTypes
[] = {"unknown", "new", "new[]", "malloc",
123 "calloc", "realloc", "delete", "delete[]", "free"};
125 AllocUnit
* hashTable
[hashSize
];
126 AllocUnit
* reservoir
;
127 uint currentAllocationCount
= 0; // tracked allocations only
128 uint breakOnAllocationCount
= 0;
130 const char * sourceFile
= "??";
131 const char * sourceFunc
= "??";
133 AllocUnit
** reservoirBuffer
= NULL
;
134 uint reservoirBufferSize
= 0;
136 bool released
= false; // do not allocate any more resources
139 * Local functions only
141 const char * SourceFileStripper(const char * file
)
143 const char * ptr
= strrchr(file
, '\\');
146 ptr
= strrchr(file
, '/');
152 const char * OwnerString(const char * srcFile
,
153 unsigned srcLine
, const char * srcFunc
)
156 memset(str
, 0, sizeof(str
));
158 sprintf(str
, "%s(%05d)::%s", SourceFileStripper(srcFile
),
163 const char * InsertCommas(unsigned value
)
166 memset(str
, 0, sizeof(str
));
167 sprintf(str
, "%u", value
);
170 memmove(&str
[strlen(str
) - 3], &str
[strlen(str
) - 4], 4);
171 str
[strlen(str
) - 4] = ',';
175 memmove(&str
[strlen(str
) - 7], &str
[strlen(str
) - 8], 8);
176 str
[strlen(str
) - 8] = ',';
180 memmove(&str
[strlen(str
) - 11], &str
[strlen(str
) - 12], 12);
181 str
[strlen(str
) - 12] = ',';
186 const char * MemorySizeString(unsigned long size
)
189 if(size
> (1024 * 1024))
190 sprintf(str
, "%10s (%7.2fM)", InsertCommas(size
),
191 static_cast<float>(size
) / (1024.0f
* 1024.0f
));
192 else if (size
> 1024)
193 sprintf(str
, "%10s (%7.2fK)", InsertCommas(size
),
194 static_cast<float>(size
) / 1024.0f
);
196 sprintf(str
, "%10s bytes ", InsertCommas(size
));
200 AllocUnit
* FindAllocUnit(const void * address
)
203 BREAK_ASSERT(address
!= NULL
);
205 // Use the address to locate the hash index. Note that we shift off
206 // the lower four bits. This is because most allocated addresses will be
207 // on four-, eight- or even sixteen-byte boundaries. If we didn't do
208 // this, the hash index would not have very good coverage.
209 uint hashIndex
= (reinterpret_cast<uptr
>(
210 address
) >> 4) & (hashSize
- 1);
211 AllocUnit
* ptr
= hashTable
[hashIndex
];
214 if (ptr
->reportedAddress
== address
)
221 size_t CalcActualSize(size_t reportedSize
)
223 return reportedSize
+ paddingSize
* sizeof(int32
) * 2;
226 size_t CalcReportedSize(const size_t actualSize
)
228 return actualSize
- paddingSize
* sizeof(int32
) * 2;
231 void * GetReportedAddress(const void * actualAddress
)
237 // Just account for the padding
238 return const_cast<byte
*>(reinterpret_cast<const byte
*>(actualAddress
))
239 + sizeof(int32
) * paddingSize
;
242 void WipeWithPattern(AllocUnit
* allocUnit
, int32 pattern
,
243 uint origReportedSize
= 0)
245 // For a serious test run, we use wipes of random a random value. However,
246 // if this causes a crash, we don't want it to crash in a differnt place
247 // each time, so we specifically DO NOT call srand. If, by chance your
248 // program calls srand(), you may wish to disable that when running with
249 // a random wipe test. This will make any crashes more consistent so they
250 // can be tracked down easier.
252 pattern
= ((rand() & 0xff) << 24) | ((rand() & 0xff) << 16)
253 | ((rand() & 0xff) << 8) | (rand() & 0xff);
256 // We should wipe with 0's if we're not in debug mode, so we can help
257 // hide bugs if possible when we release the product. So uncomment the
258 // following line for releases.
260 // Note that the "alwaysWipeAll" should be turned on for this to have
261 // effect, otherwise it won't do much good. But we'll leave it this way
262 // (as an option) because this does slow things down.
265 // This part of the operation is optional
266 if(alwaysWipeAll
&& allocUnit
->reportedSize
> origReportedSize
) {
268 int32
* lptr
= reinterpret_cast<int32
*>(allocUnit
->reportedAddress
)
270 int length
= static_cast<int>(allocUnit
->reportedSize
273 for (int i
= 0; i
< (length
>> 2); ++i
, ++lptr
) {
277 // Fill the remainder
279 char * cptr
= reinterpret_cast<char *>(lptr
);
280 for(int i
= 0; i
< (length
& 0x3); ++i
, ++cptr
, shiftCount
+= 8) {
281 *cptr
= static_cast<char>(
282 (pattern
& (0xff << shiftCount
)) >> shiftCount
);
286 // Write in the prefix/postfix bytes
287 int32
* pre
= reinterpret_cast<int32
*>(allocUnit
->actualAddress
);
288 int32
* post
= reinterpret_cast<int32
*>(reinterpret_cast<byte
*>(
289 allocUnit
->actualAddress
) + allocUnit
->actualSize
290 - paddingSize
* sizeof(int32
));
291 for(uint i
= 0; i
< paddingSize
; ++i
, ++pre
, ++post
) {
292 *pre
= prefixPattern
;
293 *post
= postfixPattern
;
297 uint
CalcUnused(const AllocUnit
* allocUnit
)
300 reinterpret_cast<const uint32
*>(allocUnit
->reportedAddress
);
303 for(uint i
= 0; i
< allocUnit
->reportedSize
;
304 i
+= sizeof(int32
), ++ptr
) {
305 if(*ptr
== unusedPattern
)
306 count
+= sizeof(int32
);
314 for (uint i
= 0; i
< hashSize
; ++i
) {
315 AllocUnit
* ptr
= hashTable
[i
];
317 // skip external allocations
326 void DumpAllocations(std::ostream
& out
)
328 out
<< "Alloc. Addr Size Addr Size "
329 " BreakOn BreakOn \n"
330 << "Number Reported Reported Actual Actual Unused "
331 "Method Dealloc Realloc Allocated by \n"
332 << "------ ---------- --------- ---------- --------- --------- "
333 "-------- ------- ------- --------------------------------"
336 for (uint i
= 0; i
< hashSize
; ++i
) {
337 AllocUnit
* ptr
= hashTable
[i
];
339 // skip external allocations
341 out
<< std::setw(6) << ptr
->allocNumber
342 << " " << std::hex
<< std::setw(10)
343 << ptr
->reportedAddress
<< " "
344 << std::setw(9) << ptr
->reportedSize
<< " "
345 << std::setw(10) << ptr
->actualAddress
<< " "
346 << std::setw(9) << ptr
->actualSize
<< " "
347 << std::setw(9) << CalcUnused(ptr
) << " "
348 << std::left
<< std::setw(8)
349 << allocationTypes
[ptr
->allocType
] << " "
350 << (ptr
->breakOnDealloc
? "Y" : "N") << " "
351 << (ptr
->breakOnRealloc
? "Y" : "N") << " "
352 << OwnerString(ptr
->srcFile
, ptr
->srcLine
, ptr
->srcFunc
)
353 << "\n" << std::dec
<< std::right
;
360 void DumpLeakReport(void)
363 uint leaks
= CalcLeaks();
365 vLog
<< "No memory leaks found." << std::endl
;
370 time_t t
= time(NULL
);
371 struct tm
* tme
= localtime(&t
);
373 vLog
<< "\n-----------------------------------------------------------"
374 "---------------------\n"
375 << " Memory leak report for: " << asctime(tme
)
376 << "-----------------------------------------------------------"
377 "---------------------\n\n";
379 vLog
<< leaks
<< " memory leak" << (leaks
== 1 ? "" : "s")
382 DumpAllocations(vLog
);
386 void ResetGlobals(void)
393 void DumpAllocUnit(const AllocUnit
* allocUnit
, const char *prefix
)
395 vLog
<< fac(lfMemory
)
396 << "[I] " << prefix
<< "Address (reported): "
397 << std::setw(10) << std::hex
<< allocUnit
->reportedAddress
398 << "\n[I] " << prefix
<< "Address (actual) : "
399 << std::setw(10) << allocUnit
->actualAddress
400 << "\n[I] " << prefix
<< "Size (reported) : "
401 << std::setw(10) << allocUnit
->reportedSize
402 << " (" << MemorySizeString(allocUnit
->reportedSize
) << ")"
403 << "\n[I] " << prefix
<< "Size (actual) : "
404 << std::setw(10) << allocUnit
->actualAddress
405 << std::setw(10) << allocUnit
->actualSize
406 << " (" << MemorySizeString(allocUnit
->actualSize
) << ")"
407 << "\n[I] " << prefix
<< "Owner : "
408 << allocUnit
->srcFile
<< allocUnit
->srcLine
<< allocUnit
->srcFunc
409 << "\n[I] " << prefix
<< "Allocation type : "
410 << allocationTypes
[allocUnit
->allocType
]
411 << "\n[I] " << prefix
<< "Allocation number : "
412 << allocUnit
->allocNumber
<< std::endl
;
417 bool ValidateAllocUnit(const AllocUnit
* allocUnit
)
419 // Make sure the padding is untouched
420 uint32
* pre
= reinterpret_cast<uint32
*>(allocUnit
->actualAddress
);
421 uint32
* post
= reinterpret_cast<uint32
*>(static_cast<byte
*>(
422 allocUnit
->actualAddress
) + allocUnit
->actualSize
423 - paddingSize
* sizeof(int32
));
424 bool errorFlag
= false;
426 for(unsigned i
= 0; i
< paddingSize
; ++i
, ++pre
, ++post
) {
427 if(*pre
!= prefixPattern
) {
428 vLog
<< fac(lfMemory
)
429 << "[!] A memory allocation unit was corrupt because "
430 "of an underrun:" << std::endl
;
431 DumpAllocUnit(allocUnit
, " ");
435 // If you hit this assert, then you should know that this allocation
436 // unit has been damaged. Something (possibly the owner?) has underrun
437 // the allocation unit (modified a few bytes prior to the start). You
438 // can interrogate the variable 'allocUnit' to see statistics and
439 // information about this damaged allocation unit.
440 BREAK_ASSERT(*pre
== prefixPattern
);
442 if(*post
!= postfixPattern
) {
443 vLog
<< fac(lfMemory
)
444 << "[!] A memory allocation unit was corrupt because "
445 "of an overerrun:" << std::endl
;
446 DumpAllocUnit(allocUnit
, " ");
450 // If you hit this assert, then you should know that this allocation
451 // unit has been damaged. Something (possibly the owner?) has overrun
452 // the allocation unit (modified a few bytes after the end). You can
453 // interrogate the variable 'allocUnit' to see statistics and
454 // information about this damaged allocation unit.
455 BREAK_ASSERT(*post
== postfixPattern
);
458 // Return the error status (we invert it, because a return
459 // of 'false' means error)
466 bool InitializeMemoryManager(const char * buildName
)
468 // Log this function to be called upon program shut down.
469 atexit(ReleaseMemoryManager
);
471 #ifdef LOG_SINGLETONS
472 vLog
<< "Initializing memory manager." << std::endl
;
473 #endif /* LOG_SINGLETONS */
477 DEBUG_ASSERT(strlen(buildName
) < 32);
478 strcpy(tmpstr
, buildName
);
479 strcat(tmpstr
, "-mem.log");
481 vLog
.AddOutput(lfMemory
, ltFile
, tmpstr
, cleanupLogOnFirstRun
);
483 if (cleanupLogOnFirstRun
) {
484 cleanupLogOnFirstRun
= false;
486 // Print a header for the log
487 vLog
<< fac(lfMemory
)
488 << "This file contains a log of all memory operations performed"
489 " during the last run.\n\nInterrogate this file to track errors or "
490 "to help track down memory-related\nissues. You can do this by tracing "
491 "the allocations performed by a specific\nowner or by tracking a "
492 "specific address through a series of allocations\nand reallocations.\n"
493 "\nThere is a lot of useful information here which, when used "
494 "creatively, can be\nextremely helpful.\n\nNote that the following \n"
495 "guides are used throughout this file:\n\n";
497 vLog
<< " [!] - Error\n"
498 << " [+] - Allocation\n"
499 << " [~] - Reallocation\n"
500 << " [-] - Deallocation'n"
501 << " [I] - Generic information\n"
502 << " [F] - Failure induced for the purpose of "
503 "stress-testing your application\n"
504 << " [D] - Information used for debugging \n"
505 "this memory manager\n\n";
507 vLog
<< "...so, to find all errors in the file, search for \"[!]\"";
509 vLog
<< "\n--------------------------------------------------"
510 "------------------------------" << std::endl
;
515 void ReleaseMemoryManager(void)
517 #ifdef LOG_SINGLETONS
518 vLog
<< "Releasing memory manager." << std::endl
;
519 #endif /* LOG_SINGLETONS */
523 // We can finally free up our own memory allocations
524 if(reservoirBuffer
) {
525 for(uint i
= 0; i
< reservoirBufferSize
; ++i
) {
526 free(reservoirBuffer
[i
]);
528 free(reservoirBuffer
);
530 reservoirBufferSize
= 0;
540 void SetAlwaysValidateAll(bool st
)
542 // Force a validation of all allocation units each time
543 // we enter this software
544 alwaysValidateAll
= st
;
547 void SetAlwaysLogAll(bool st
)
549 // Force a log of every allocation & deallocation into memory.log
553 void SetAlwaysWipeAll(bool st
)
555 // Force this software to always wipe memory with a pattern when
556 // it is being allocated/dallocated
560 void SetRandomWipe(bool st
)
562 // Force this software to use a random pattern when wiping memory
563 // -- good for stress testing
567 void SetBreakOnRealloc(void * address
, bool st
)
569 // Locate the existing allocation unit
570 AllocUnit
* au
= FindAllocUnit(address
);
572 // If you hit this assert, you tried to set a breakpoint on reallocation
573 // for an address that doesn't exist. Interrogate the stack frame or
574 // the variable 'au' to see which allocation this is.
575 BREAK_ASSERT(au
!= NULL
);
577 // If you hit this assert, you tried to set a breakpoint on reallocation
578 // for an address that wasn't allocated in a way that is compatible
579 // with reallocation.
580 BREAK_ASSERT(au
->allocType
== atMalloc
||
581 au
->allocType
== atCalloc
||
582 au
->allocType
== atRealloc
);
584 au
->breakOnRealloc
= st
;
587 void BreakOnDealloc(void * address
, bool st
)
589 // Locate the existing allocation unit
590 AllocUnit
* au
= FindAllocUnit(address
);
592 // If you hit this assert, you tried to set a breakpoint on deallocation
593 // for an address that doesn't exist. Interrogate the stack frame or the
594 // variable 'au' to see which allocation this is.
595 BREAK_ASSERT(au
!= NULL
);
597 au
->breakOnDealloc
= st
;
600 void BreakOnAllocation(unsigned int count
)
602 breakOnAllocationCount
= count
;
608 void SetOwner(const char * file
, unsigned line
, const char * func
)
610 if(sourceLine
&& alwaysLogAll
) {
611 vLog
<< fac(lfMemory
)
612 << "[I] NOTE! Possible destructor chain: previous owner is "
613 << OwnerString(sourceFile
, sourceLine
, sourceFunc
) << std::endl
;
616 // Okay... save this stuff off so we can keep track of the caller
623 * Allocate memory and track it
625 void * Allocator(const char * file
, unsigned line
,
626 const char * func
, AllocType type
, size_t size
)
629 // requesting memory at the program cleanup?
630 BREAK_ASSERT(!released
);
632 // Increase our allocation count
634 ++currentAllocationCount
;
637 if(line
&& alwaysLogAll
) {
638 vLog
<< fac(lfMemory
)
639 << "[+] " << std::setw(5)
640 << currentAllocationCount
<< " " << std::setw(8)
641 << allocationTypes
[type
]
642 << " of size " << std::hex
<< std::setw(8)
643 << size
<< std::dec
<< "("
644 << std::setw(8) << size
<< ") by "
645 << OwnerString(file
, line
, func
) << std::endl
;
648 // If you hit this assert, you requested a breakpoint on
649 // a specific allocation count
650 BREAK_ASSERT(!line
|| currentAllocationCount
!= breakOnAllocationCount
);
652 // If necessary, grow the reservoir of unused allocation units
654 // Allocate 256 reservoir elements
655 reservoir
= (AllocUnit
*) malloc(sizeof(AllocUnit
) * 256);
657 // If you hit this assert, then the memory manager failed to allocate
658 // internal memory for tracking the allocations
659 BREAK_ASSERT(reservoir
!= NULL
);
661 // Danger Will Robinson!
662 if(reservoir
== NULL
)
663 throw "Unable to allocate RAM for internal memory tracking data";
665 // Build a linked-list of the elements in our reservoir
666 memset(reservoir
, 0, sizeof(AllocUnit
) * 256);
667 for (unsigned int i
= 0; i
< 256 - 1; ++i
) {
668 reservoir
[i
].next
= &reservoir
[i
+ 1];
671 // Add this address to our reservoirBuffer so we can free it later
672 AllocUnit
** temp
= (AllocUnit
**) realloc(reservoirBuffer
,
673 (reservoirBufferSize
+ 1) * sizeof(AllocUnit
*));
677 reservoirBuffer
= temp
;
678 reservoirBuffer
[reservoirBufferSize
++] = reservoir
;
682 // Logical flow says this should never happen...
683 BREAK_ASSERT(reservoir
!= NULL
);
685 // Grab a new allocaton unit from the front of the reservoir
686 AllocUnit
* au
= reservoir
;
687 reservoir
= au
->next
;
689 // Populate it with some real data
690 memset(au
, 0, sizeof(AllocUnit
));
691 au
->actualSize
= CalcActualSize(size
);
692 #ifdef RANDOM_FAILURE
694 double b
= RAND_MAX
/ 100.0 * RANDOM_FAILURE
;
696 au
->actualAddress
= malloc(au
->actualSize
);
698 vLog
<< fac(lfMemory
) << "[F] Random faiure" << std::endl
;
699 au
->actualAddress
= NULL
;
701 #else /* RANDOM_FAILURE */
702 au
->actualAddress
= malloc(au
->actualSize
);
703 #endif /* RANDOM_FAILURE */
704 au
->reportedSize
= size
;
705 au
->reportedAddress
= GetReportedAddress(au
->actualAddress
);
706 au
->allocType
= type
;
708 au
->allocNumber
= currentAllocationCount
;
711 strncpy(au
->srcFile
, SourceFileStripper(file
),
712 sizeof(au
->srcFile
) - 1);
714 strcpy(au
->srcFile
, "??");
717 strncpy(au
->srcFunc
, func
, sizeof(au
->srcFunc
) - 1);
719 strcpy(au
->srcFunc
, "??");
721 // We don't want to assert with random failures, because we want
722 // the application to deal with them.
723 #ifndef RANDOM_FAILURE
724 // If you hit this assert, then the requested allocation simply failed
725 // (you're out of memory.) Interrogate the variable 'au' or the stack
726 // frame to see what you were trying to do.
727 BREAK_ASSERT(au
->actualAddress
!= NULL
);
728 #endif /* RANDOM_FAILURE */
730 if (au
->actualAddress
== NULL
) {
731 throw "Request for allocation failed. Out of memory.";
734 // If you hit this assert, then this allocation was made from a source
735 // that isn't setup to use this memory tracking software, use the stack
736 // frame to locate the source and include our H file.
737 BREAK_ASSERT(type
!= atUnknown
);
739 // Insert the new allocation into the hash table
740 unsigned hashIndex
= (reinterpret_cast<uptr
>(
741 au
->reportedAddress
) >> 4) & (hashSize
- 1);
742 if(hashTable
[hashIndex
])
743 hashTable
[hashIndex
]->prev
= au
;
744 au
->next
= hashTable
[hashIndex
];
746 hashTable
[hashIndex
] = au
;
748 // Account for the new allocation unit in our stats
749 stats
.totalReportedMemory
+= static_cast<unsigned int>(au
->reportedSize
);
750 stats
.totalActualMemory
+= static_cast<unsigned int>(au
->actualSize
);
751 ++stats
.totalAllocUnitCount
;
753 if(stats
.totalReportedMemory
> stats
.peakReportedMemory
)
754 stats
.peakReportedMemory
= stats
.totalReportedMemory
;
755 if(stats
.totalActualMemory
> stats
.peakActualMemory
)
756 stats
.peakActualMemory
= stats
.totalActualMemory
;
757 if(stats
.totalAllocUnitCount
> stats
.peakAllocUnitCount
)
758 stats
.peakAllocUnitCount
= stats
.totalAllocUnitCount
;
760 stats
.accumReportedMemory
+= static_cast<unsigned int>(au
->reportedSize
);
761 stats
.accumActualMemory
+= static_cast<unsigned int>(au
->actualSize
);
762 ++stats
.accumAllocUnitCount
;
764 // Prepare the allocation unit for use (wipe it with recognizable garbage)
765 WipeWithPattern(au
, unusedPattern
);
767 // calloc() expects the reported memory
768 // address range to be filled with 0's
769 if(type
== atCalloc
) {
770 memset(au
->reportedAddress
, 0, au
->reportedSize
);
773 // Validate every single allocated unit in memory
774 if(alwaysValidateAll
)
775 ValidateAllAllocUnits();
778 if(line
&& alwaysLogAll
) {
779 vLog
<< fac(lfMemory
) << "[+] ----> addr "
780 << std::hex
<< std::setw(8)
781 << au
->reportedAddress
<< std::dec
<< std::endl
;
784 // Resetting the globals insures that if at some later time, somebody
785 // calls our memory manager from an unknown source (i.e. they didn't
786 // include our H file) then we won't think it was the last allocation.
789 // Return the (reported) address of the new allocation unit
790 return au
->reportedAddress
;
791 } catch(const char *err
) {
792 // Deal with the errors
793 vLog
<< fac(lfMemory
) << "[!] " << err
<< std::endl
;
801 * Reallocate memory and track it
803 void * Reallocator(const char * file
, unsigned line
,
804 const char * func
, AllocType type
, size_t size
, void * address
)
807 // Calling realloc with a NULL should force same operations as a malloc
809 void * res
= Allocator(file
, line
, func
, type
, size
? size
: 1);
814 Deallocator(file
, line
, func
, atRealloc
, address
);
818 // requesting memory at the program cleanup?
819 BREAK_ASSERT(!released
);
821 // Increase our allocation count
823 ++currentAllocationCount
;
825 // If you hit this assert, you requested a breakpoint
826 // on a specific allocation count
827 BREAK_ASSERT(currentAllocationCount
!= breakOnAllocationCount
);
830 if (line
&& alwaysLogAll
) {
831 vLog
<< fac(lfMemory
)
832 << "[+] " << std::setw(5)
833 << currentAllocationCount
<< " " << std::setw(8)
834 << allocationTypes
[type
] << " of size "
835 << std::hex
<< std::setw(8) << size
<< std::dec
<< "("
836 << std::setw(8) << size
<< ") by "
837 << OwnerString(file
, line
, func
) << std::endl
;
840 // Locate the existing allocation unit
841 AllocUnit
* au
= FindAllocUnit(address
);
843 // If you hit this assert, you tried to reallocate RAM that
844 // wasn't allocated by this memory manager.
845 BREAK_ASSERT(au
!= NULL
);
847 throw "Request to reallocate RAM that was never allocated";
849 // If you hit this assert, then the allocation unit that is about to
850 // be reallocated is damaged. But you probably already know that from
851 // a previous assert you should have seen in validateAllocUnit() :)
852 BREAK_ASSERT(ValidateAllocUnit(au
));
854 // If you hit this assert, then this reallocation was made from
855 // a source that isn't setup to use this memory tracking software,
856 // use the stack frame to locate the source and include our H file.
857 BREAK_ASSERT(type
!= atUnknown
);
859 // If you hit this assert, you were trying to reallocate RAM that
860 // was not allocated in a way that is compatible with
861 // realloc. In other words, you have a allocation/reallocation mismatch.
862 BREAK_ASSERT(au
->allocType
== atMalloc
||
863 au
->allocType
== atCalloc
||
864 au
->allocType
== atRealloc
);
866 // If you hit this assert, then the "break on realloc" flag for this
867 // allocation unit is set (and will continue to be set until you
868 // specifically shut it off. Interrogate the 'au' variable to
869 // determine information about this allocation unit.
870 BREAK_ASSERT(au
->breakOnRealloc
== false);
872 // Keep track of the original size
873 unsigned origReportedSize
= static_cast<unsigned int>(au
->reportedSize
);
875 if(line
&& alwaysLogAll
)
876 vLog
<< fac(lfMemory
) << "[+] ----> addr "
877 << std::hex
<< std::setw(8)
878 << origReportedSize
<< std::dec
<< "(" << std::setw(8)
879 << origReportedSize
<< ")" << std::endl
;
881 // Do the reallocation
882 void * oldReportedAddress
= address
;
883 size_t newActualSize
= CalcActualSize(size
);
884 void * newActualAddress
= NULL
;
885 #ifdef RANDOM_FAILURE
887 double b
= RAND_MAX
/ 100.0 * RANDOM_FAILURE
;
889 newActualAddress
= realloc(au
->actualAddress
, newActualSize
);
891 vLog
<< fac(lfMemory
) << "[F] Random faiure" << std::endl
;
893 #else /* RANDOM_FAILURE */
894 newActualAddress
= realloc(au
->actualAddress
, newActualSize
);
895 #endif /* RANDOM_FAILURE */
897 // We don't want to assert with random failures, because we want
898 // the application to deal with them.
900 #ifndef RANDOM_FAILURE
901 // If you hit this assert, then the requested allocation simply
902 // failed (you're out of memory) Interrogate the variable 'au' to see
903 // the original allocation. You can also query 'newActualSize' to see
904 // the amount of memory trying to be allocated. Finally, you can query
905 // 'reportedSize' to see how much memory was requested by the caller.
906 BREAK_ASSERT(newActualAddress
);
907 #endif /* RANDOM_FAILURE */
909 if(!newActualAddress
)
910 throw "Request for reallocation failed. Out of memory.";
912 // Remove this allocation from our stats (we'll add the
913 // new reallocation again later)
914 stats
.totalReportedMemory
-= static_cast<unsigned int>(au
->reportedSize
);
915 stats
.totalActualMemory
-= static_cast<unsigned int>(au
->actualSize
);
917 // Update the allocation with the new information
919 au
->actualSize
= newActualSize
;
920 au
->actualAddress
= newActualAddress
;
921 au
->reportedSize
= CalcReportedSize(newActualSize
);
922 au
->reportedAddress
= GetReportedAddress(newActualAddress
);
923 au
->allocType
= type
;
925 au
->allocNumber
= currentAllocationCount
;
928 strncpy(au
->srcFile
, SourceFileStripper(file
), sizeof(au
->srcFile
) - 1);
930 strcpy(au
->srcFile
, "??");
933 strncpy(au
->srcFunc
, func
, sizeof(au
->srcFunc
) - 1);
935 strcpy(au
->srcFunc
, "??");
937 // The reallocation may cause the address to change, so we
938 // should relocate our allocation unit within the hash table
939 if(oldReportedAddress
!= au
->reportedAddress
) {
940 // Remove this allocation unit from the hash table
941 unsigned hashIndex
= (reinterpret_cast<uptr
>(
942 oldReportedAddress
) >> 4) & (hashSize
- 1);
943 if(hashTable
[hashIndex
] == au
) {
944 hashTable
[hashIndex
] = hashTable
[hashIndex
]->next
;
947 au
->prev
->next
= au
->next
;
949 au
->next
->prev
= au
->prev
;
952 // Re-insert it back into the hash table
953 hashIndex
= (reinterpret_cast<uptr
>(
954 au
->reportedAddress
) >> 4) & (hashSize
- 1);
955 if(hashTable
[hashIndex
])
956 hashTable
[hashIndex
]->prev
= au
;
957 au
->next
= hashTable
[hashIndex
];
959 hashTable
[hashIndex
] = au
;
962 // Account for the new allocatin unit in our stats
963 stats
.totalReportedMemory
+= static_cast<unsigned int>(au
->reportedSize
);
964 stats
.totalActualMemory
+= static_cast<unsigned int>(au
->actualSize
);
966 if (stats
.totalReportedMemory
> stats
.peakReportedMemory
)
967 stats
.peakReportedMemory
= stats
.totalReportedMemory
;
968 if(stats
.totalActualMemory
> stats
.peakActualMemory
)
969 stats
.peakActualMemory
= stats
.totalActualMemory
;
971 int deltaReportedSize
= static_cast<int>(
972 size
- origReportedSize
);
974 if(deltaReportedSize
> 0) {
975 stats
.accumReportedMemory
+= deltaReportedSize
;
976 stats
.accumActualMemory
+= deltaReportedSize
;
979 // Prepare the allocation unit for use (wipe it with recognizable garbage)
980 WipeWithPattern(au
, unusedPattern
, origReportedSize
);
982 // If you hit this assert, then something went wrong, because
983 // the allocation unit was properly validated PRIOR to the reallocation.
984 // This should not happen.
985 BREAK_ASSERT(ValidateAllocUnit(au
));
987 // Validate every single allocated unit in memory
988 if (alwaysValidateAll
)
989 ValidateAllAllocUnits();
992 if(line
&& alwaysLogAll
) {
993 vLog
<< fac(lfMemory
) << "[+] ----> addr "
994 << std::hex
<< std::setw(8)
995 << reinterpret_cast<uptr
>(au
->reportedAddress
)
996 << std::dec
<< std::endl
;
999 // Resetting the globals insures that if at some later time, somebody
1000 // calls our memory manager from an unknown source (i.e. they didn't
1001 // include our H file) then we won't think it was the last allocation.
1004 // Return the (reported) address of the new allocation unit
1005 return au
->reportedAddress
;
1006 } catch(const char *err
) {
1007 // Deal with the errors
1008 vLog
<< fac(lfMemory
) << "[!] " << err
<< std::endl
;
1016 * Deallocate memory and track it
1018 void Deallocator(const char * file
, unsigned line
,
1019 const char * func
, AllocType type
, const void * address
)
1022 // determine the allocator, since stack may get confused
1023 AllocUnit
* au
= NULL
;
1025 au
= FindAllocUnit(address
);
1027 // it's an external allocation
1032 if(au
&& au
->srcLine
&& alwaysLogAll
) {
1033 vLog
<< fac(lfMemory
)
1034 << "[-] ----- " << std::setw(8)
1035 << allocationTypes
[type
] << " of addr "
1036 << std::hex
<< std::setw(8) << address
1037 << " by " << std::dec
1038 << OwnerString(file
, line
, func
)
1042 // We should only ever get here with a null pointer if they try to do
1043 // so with a call to free() (delete[] and delete will both bail before
1044 // they get here.) So, since ANSI allows free(NULL), we'll not bother
1045 // trying to actually free the allocated memory or track it any further.
1047 // If you hit this assert, you tried to deallocate RAM that
1048 // wasn't allocated by this memory manager.
1049 // HACK: Wave somehow manages to avoid our overloaded new operators
1050 BREAK_ASSERT(au
!= NULL
|| line
== 0);
1052 throw "Request to deallocate RAM that was never allocated";
1054 // If you hit this assert, then the allocation unit that is about to
1055 // be deallocated is damaged. But you probably already know that from
1056 // a previous assert you should have seen in validateAllocUnit() :)
1057 BREAK_ASSERT(ValidateAllocUnit(au
));
1059 // If you hit this assert, then this deallocation was made from a
1060 // source that isn't setup to use this memory tracking software, use
1061 // the stack frame to locate the source and include our H file.
1062 BREAK_ASSERT(type
!= atUnknown
);
1064 // If you hit this assert, you were trying to deallocate RAM that was
1065 // not allocated in a way that is compatible with the deallocation
1066 // method requested. In other words, you have
1067 // a allocation/deallocation mismatch.
1068 BREAK_ASSERT((type
== atDelete
&& atNew
) ||
1069 (type
== atDeleteArray
&& au
->allocType
== atNewArray
) ||
1070 (type
== atFree
&& au
->allocType
== atMalloc
) ||
1071 (type
== atFree
&& au
->allocType
== atCalloc
) ||
1072 (type
== atFree
&& au
->allocType
== atRealloc
) ||
1073 (type
== atRealloc
&& au
->allocType
== atMalloc
) ||
1074 (type
== atRealloc
&& au
->allocType
== atCalloc
) ||
1075 (type
== atRealloc
&& au
->allocType
== atRealloc
) ||
1076 (type
== atUnknown
));
1078 // If you hit this assert, then the "break on dealloc" flag for this
1079 // allocation unit is set. Interrogate the 'au' variable to determine
1080 // information about this allocation unit.
1081 BREAK_ASSERT(au
->breakOnDealloc
== false);
1083 // Wipe the deallocated RAM with a new pattern. This doen't actually
1084 // do us much good in debug mode under WIN32, because Microsoft's
1085 // memory debugging & tracking utilities will wipe it right after
1087 WipeWithPattern(au
, releasedPattern
);
1089 // Do the deallocation
1090 free(au
->actualAddress
);
1092 // Remove this allocation unit from the hash table
1093 unsigned hashIndex
= (reinterpret_cast<uptr
>(
1094 au
->reportedAddress
) >> 4) & (hashSize
- 1);
1095 if(hashTable
[hashIndex
] == au
) {
1096 hashTable
[hashIndex
] = au
->next
;
1098 if (au
->prev
) au
->prev
->next
= au
->next
;
1099 if (au
->next
) au
->next
->prev
= au
->prev
;
1102 // Remove this allocation from our stats
1103 stats
.totalReportedMemory
1104 -= static_cast<unsigned int>(au
->reportedSize
);
1105 stats
.totalActualMemory
1106 -= static_cast<unsigned int>(au
->actualSize
);
1107 stats
.totalAllocUnitCount
--;
1109 // Add this allocation unit to the front of our
1110 // reservoir of unused allocation units
1111 memset(au
, 0, sizeof(AllocUnit
));
1112 au
->next
= reservoir
;
1116 // Resetting the globals insures that if at some later time, somebody
1117 // calls our memory manager from an unknown source (i.e. they didn't
1118 // include our H file) then we won't think it was the last allocation.
1121 // Validate every single allocated unit in memory
1122 if (alwaysValidateAll
)
1123 ValidateAllAllocUnits();
1124 } catch(const char *err
) {
1126 vLog
<< fac(lfMemory
) << "[!] " << err
<< std::endl
;
1132 * The following utilitarian allow you to become proactive in tracking your
1133 * own memory, or help you narrow in on those tough bugs.
1135 bool ValidateAddress(const void * address
)
1137 // Just see if the address exists in our allocation routines
1138 return FindAllocUnit(address
) != NULL
;
1141 bool ValidateAllAllocUnits(void)
1143 // Just go through each allocation unit in the hash table and
1144 // count the ones that have errors
1145 unsigned errors
= 0;
1146 unsigned allocCount
= 0;
1148 for (unsigned i
= 0; i
< hashSize
; ++i
) {
1149 AllocUnit
* ptr
= hashTable
[i
];
1152 if(!ValidateAllocUnit(ptr
))
1158 // Test for hash-table correctness
1159 if(allocCount
!= stats
.totalAllocUnitCount
) {
1160 vLog
<< fac(lfMemory
)
1161 << "[!] Memory tracking hash table corrupt!" << std::endl
;
1165 // If you hit this assert, then the internal memory (hash table) used by
1166 // this memory tracking software is damaged! The best way to track this
1167 // down is to use the alwaysLogAll flag in conjunction with STRESS_TEST
1168 // macro to narrow in on the offending code. After running the application
1169 // with these settings (and hitting this assert again), interrogate the
1170 // memory.log file to find the previous successful operation. The
1171 // corruption will have occurred between that point and this assertion.
1172 BREAK_ASSERT(allocCount
== stats
.totalAllocUnitCount
);
1174 // If you hit this assert, then you've probably already been notified that
1175 // there was a problem with a allocation unit in a prior call to
1176 // validateAllocUnit(), but this assert is here just to make sure you
1177 // know about it. :)
1178 BREAK_ASSERT(errors
== 0);
1182 vLog
<< fac(lfMemory
)
1183 << "[!] While validating all allocation units, " << errors
1184 << " allocation unit(s) were found to have problems" << std::endl
;
1186 // Return the error status
1191 * Unused RAM calculation routines. Use these to determine how
1192 * much of your RAM is unused (in bytes)
1194 unsigned CalcAllUnused(void)
1196 // Just go through each allocation unit in the hash
1197 // table and count the unused RAM
1200 for (unsigned i
= 0; i
< hashSize
; ++i
) {
1201 AllocUnit
* ptr
= hashTable
[i
];
1203 total
+= CalcUnused(ptr
);
1211 * The following functions are for logging and statistics reporting.
1213 void DumpLogReport(std::ostream
& out
)
1215 time_t t
= time(NULL
);
1216 struct tm
* tme
= localtime(&t
);
1218 // logging into the default facility on purpose
1219 out
<< "\n-----------------------------------------------------------"
1220 "---------------------\n"
1221 << " Memory leak report for: " << asctime(tme
)
1222 << "-----------------------------------------------------------"
1223 "---------------------\n\n";
1226 out
<< " T O T A L M E M O R Y U S A G E\n"
1227 << "-----------------------------------------------------------"
1228 "---------------------\n"
1229 << " Allocation unit count: "
1230 << std::setw(10) << InsertCommas(stats
.totalAllocUnitCount
);
1231 out
<< "\n Reported to application: "
1232 << MemorySizeString(stats
.totalReportedMemory
);
1233 out
<< "\n Actual total memory in use: "
1234 << MemorySizeString(stats
.totalActualMemory
);
1235 out
<< "\n Memory tracking overhead: "
1236 << MemorySizeString(stats
.totalActualMemory
1237 - stats
.totalReportedMemory
) << "\n\n";
1239 out
<< " P E A K M E M O R Y U S A G E\n"
1240 << "-----------------------------------------------------------"
1241 "---------------------\n"
1242 << " Allocation unit count: "
1243 << std::setw(10) << InsertCommas(stats
.peakAllocUnitCount
);
1244 out
<< "\n Reported to application: "
1245 << MemorySizeString(stats
.peakReportedMemory
);
1246 out
<< "\n Actual: "
1247 << MemorySizeString(stats
.peakActualMemory
);
1248 out
<< "\n Memory tracking overhead: "
1249 << MemorySizeString(stats
.peakActualMemory
1250 - stats
.peakReportedMemory
) << "\n\n";
1252 out
<< " A C C U M U L A T E D M E M O R Y U S A G E\n"
1253 << "-----------------------------------------------------------"
1254 "---------------------\n"
1255 << " Allocation unit count: "
1256 << std::setw(10) << InsertCommas(stats
.accumAllocUnitCount
);
1257 out
<< "\n Reported to application: "
1258 << MemorySizeString(stats
.accumReportedMemory
);
1259 out
<< "\n Actual: "
1260 << MemorySizeString(stats
.accumActualMemory
) << "\n\n";
1262 uint unused
= CalcAllUnused();
1263 float unused_perc
= (float)unused
/ stats
.totalReportedMemory
;
1265 out
<< " U N U S E D M E M O R Y\n"
1266 << "-----------------------------------------------------------"
1267 "---------------------\n"
1268 << " Percentage of allocated memory actually used: "
1269 << std::setw(10) << std::setprecision(2)
1270 << (1.0f
- unused_perc
) * 100.0f
<< " %\n"
1271 << " Percentage of allocated memory not used: "
1272 << std::setw(10) << std::setprecision(2)
1273 << unused_perc
* 100.0f
<< " %\n"
1274 << " Memory allocated but not actually used: "
1275 << MemorySizeString(unused
) << "\n\n";
1278 MemStats
GetMemoryStatistics(void)
1286 * Global new/new[] - These are the standard new/new[] operators. They are
1287 * merely interface functions that operate like normal new/new[], but use our
1288 * memory tracking routines.
1290 void * operator new (size_t size
) throw(std::bad_alloc
)
1292 // Save these off...
1293 const char * file
= tre::sourceFile
;
1294 unsigned line
= tre::sourceLine
;
1295 const char * func
= tre::sourceFunc
;
1297 // ANSI says: allocation requests of 0 bytes will still return a valid value
1301 // ANSI says: loop continuously because the error handler
1302 // could possibly free up some memory
1305 // Try the allocation
1306 void * ptr
= tre::Allocator(file
, line
, func
, tre::atNew
, size
);
1311 // There isn't a way to determine the new handler, except through
1312 // setting it. So we'll just set it to NULL, then set it back again.
1313 std::new_handler nh
= std::set_new_handler(0);
1314 std::set_new_handler(nh
);
1316 // If there is an error handler, call it
1320 // Otherwise, throw the exception
1322 throw std::bad_alloc();
1327 void * operator new [] (size_t size
) throw(std::bad_alloc
)
1329 // Save these off...
1330 const char * file
= tre::sourceFile
;
1331 unsigned line
= tre::sourceLine
;
1332 const char * func
= tre::sourceFunc
;
1334 // The ANSI standard says that allocation requests of 0 bytes will still
1335 // return a valid value
1339 // ANSI says: loop continuously because the error handler could
1340 // possibly free up some memory
1342 // Try the allocation
1343 void * ptr
= tre::Allocator(file
, line
, func
, tre::atNewArray
, size
);
1348 // There isn't a way to determine the new handler, except through setting
1349 // it. So we'll just set it to NULL, then set it back again.
1351 std::new_handler nh
= std::set_new_handler(0);
1352 std::set_new_handler(nh
);
1354 // If there is an error handler, call it
1357 // Otherwise, throw the exception
1359 throw std::bad_alloc();
1365 * Other global new/new[] - These are the standard new/new[] operators as
1366 * used by Microsoft's memory tracker. We don't want them interfering with
1367 * our memory tracking efforts. Like the previous versions, these are merely
1368 * interface functions that operate like normal new/new[], but use
1369 * our memory tracking routines.
1371 void * operator new(size_t size
,
1372 const char * file
, int line
) throw(std::bad_alloc
)
1374 // The ANSI standard says that allocation requests of 0 bytes will
1375 // still return a valid value
1379 // ANSI says: loop continuously because the error handler could
1380 // possibly free up some memory
1382 // Try the allocation
1383 void * ptr
= tre::Allocator(file
, line
, "??", tre::atNew
, size
);
1388 // There isn't a way to determine the new handler, except through setting
1389 // it. So we'll just set it to NULL, then set it back again.
1390 std::new_handler nh
= std::set_new_handler(0);
1391 std::set_new_handler(nh
);
1393 // If there is an error handler, call it
1396 // Otherwise, throw the exception
1398 throw std::bad_alloc();
1403 void * operator new [] (size_t size
,
1404 const char * file
, int line
) throw(std::bad_alloc
)
1406 // The ANSI standard says that allocation requests of 0 bytes will
1407 // still return a valid value
1411 // ANSI says: loop continuously because the error handler could
1412 // possibly free up some memory
1414 // Try the allocation
1415 void * ptr
= tre::Allocator(file
, line
, "??", tre::atNewArray
, size
);
1420 // There isn't a way to determine the new handler, except through
1421 // setting it. So we'll just set it to NULL, then set it back again.
1422 std::new_handler nh
= std::set_new_handler(0);
1423 std::set_new_handler(nh
);
1425 // If there is an error handler, call it
1429 // Otherwise, throw the exception
1431 throw std::bad_alloc();
1437 * Global delete/delete[] - These are the standard delete/delete[] operators.
1438 * They are merely interface functions that operate like normal delete/delete[],
1439 * but use our memory tracking routines.
1441 void operator delete(void * address
) throw()
1443 // ANSI says: delete & delete[] allow NULL pointers (they do nothing)
1445 tre::Deallocator(tre::sourceFile
, tre::sourceLine
,
1446 tre::sourceFunc
, tre::atDelete
, address
);
1447 else if(tre::alwaysLogAll
)
1448 tre::vLog
<< tre::fac(tre::lfMemory
) << "[-] ----- "
1449 << tre::allocationTypes
[tre::atDelete
]
1450 << " of NULL by " << tre::OwnerString(
1451 tre::sourceFile
, tre::sourceLine
, tre::sourceFunc
) << std::endl
;
1453 // Resetting the globals insures that if at some later time, somebody
1454 // calls our memory manager from an unknown source (i.e. they didn't include
1455 // our H file) then we won't think it was the last allocation.
1456 tre::ResetGlobals();
1459 void operator delete [] (void * address
) throw()
1461 // ANSI says: delete & delete[] allow NULL pointers (they do nothing)
1463 tre::Deallocator(tre::sourceFile
, tre::sourceLine
, tre::sourceFunc
,
1464 tre::atDeleteArray
, address
);
1465 else if(tre::alwaysLogAll
)
1466 tre::vLog
<< tre::fac(tre::lfMemory
) << "[-] ----- "
1467 << tre::allocationTypes
[tre::atDeleteArray
]
1469 << tre::OwnerString(tre::sourceFile
,
1470 tre::sourceLine
, tre::sourceFunc
);
1472 // Resetting the globals insures that if at some later time, somebody
1473 // calls our memory manager from an unknown source (i.e. they didn't include
1474 // our H file) then we won't think it was the last allocation.
1475 tre::ResetGlobals();
1478 // These routines should never get called, unless an error occures during the
1479 // allocation process. These need to be defined to make Visual C++ happy
1480 // If there was an allocation problem these method would be called
1481 // automatically by the operating system. C/C++ Users Journal (Vol. 19
1482 // No. 4 -> April 2001 pg. 60) has an excellent explanation of what is going
1484 /// Make compiler happy!
1485 void operator delete(void * address
, const char *, int) throw()
1490 /// Make compiler happy!
1491 void operator delete[](void * address
, const char *, int) throw()
1496 #endif /* MEM_MANAGER_ON */