Initial commit, includes Lua with broken Luabind as a backup for branching purposes
[terrastrategy.git] / src / memory / mmgr.cpp
blobd58bab35e5b8dc35d16cc6de76aa67f50d2c1110
1 //
2 // Copyright (C) 2008 by Martin Moracek
3 //
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.
8 //
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 // ----------------------------------------------------------------------------
27 /**
28 * @file mmgr.cpp
30 * Contains implementation of memory management.
33 #include <stdio.h>
34 #include <stdlib.h>
35 #include <string.h>
36 #include <stdarg.h>
37 #include <time.h>
38 #include <iomanip>
40 #include "vfs/logfile.h"
42 #include "platform.h"
43 #include "assertion.h"
44 #include "types.h"
45 #include "config.h"
47 #ifdef MEM_MANAGER_ON
49 #include "memory/mmgr.h"
51 #include "mem_off.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
68 namespace tre {
70 namespace {
72 struct AllocUnit {
73 size_t actualSize;
74 size_t reportedSize;
75 void * actualAddress;
76 void * reportedAddress;
77 char srcFile[40];
78 char srcFunc[40];
79 uint srcLine;
80 ::tre::AllocType allocType;
81 bool breakOnDealloc;
82 bool breakOnRealloc;
83 uint allocNumber;
85 AllocUnit * next;
86 AllocUnit * prev;
90 * Locals -- modify these flags to suit your needs
92 #ifdef STRESS_TEST
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;
119 * Other locals
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;
129 MemStats stats;
130 const char * sourceFile = "??";
131 const char * sourceFunc = "??";
132 uint sourceLine = 0;
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, '\\');
144 if(ptr)
145 return ptr + 1;
146 ptr = strrchr(file, '/');
147 if(ptr)
148 return ptr + 1;
149 return file;
152 const char * OwnerString(const char * srcFile,
153 unsigned srcLine, const char * srcFunc)
155 static char str[90];
156 memset(str, 0, sizeof(str));
158 sprintf(str, "%s(%05d)::%s", SourceFileStripper(srcFile),
159 srcLine, srcFunc);
160 return str;
163 const char * InsertCommas(unsigned value)
165 static char str[30];
166 memset(str, 0, sizeof(str));
167 sprintf(str, "%u", value);
168 if(strlen(str) > 3)
170 memmove(&str[strlen(str) - 3], &str[strlen(str) - 4], 4);
171 str[strlen(str) - 4] = ',';
173 if(strlen(str) > 7)
175 memmove(&str[strlen(str) - 7], &str[strlen(str) - 8], 8);
176 str[strlen(str) - 8] = ',';
178 if(strlen(str) > 11)
180 memmove(&str[strlen(str) - 11], &str[strlen(str) - 12], 12);
181 str[strlen(str) - 12] = ',';
183 return str;
186 const char * MemorySizeString(unsigned long size)
188 static char str[90];
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);
195 else
196 sprintf(str, "%10s bytes ", InsertCommas(size));
197 return str;
200 AllocUnit * FindAllocUnit(const void * address)
202 // Just in case...
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];
213 while(ptr) {
214 if (ptr->reportedAddress == address)
215 return ptr;
216 ptr = ptr->next;
218 return NULL;
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)
233 // We allow this...
234 if(!actualAddress)
235 return NULL;
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.
251 if(randomWipe) {
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.
263 // pattern = 0;
265 // This part of the operation is optional
266 if(alwaysWipeAll && allocUnit->reportedSize > origReportedSize) {
267 // Fill the bulk
268 int32 * lptr = reinterpret_cast<int32*>(allocUnit->reportedAddress)
269 + origReportedSize;
270 int length = static_cast<int>(allocUnit->reportedSize
271 - origReportedSize);
273 for (int i = 0; i < (length >> 2); ++i, ++lptr) {
274 *lptr = pattern;
277 // Fill the remainder
278 uint shiftCount = 0;
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)
299 const uint32 * ptr =
300 reinterpret_cast<const uint32*>(allocUnit->reportedAddress);
301 uint count = 0;
303 for(uint i = 0; i < allocUnit->reportedSize;
304 i += sizeof(int32), ++ptr) {
305 if(*ptr == unusedPattern)
306 count += sizeof(int32);
308 return count;
311 uint CalcLeaks(void)
313 uint res = 0;
314 for (uint i = 0; i < hashSize; ++i) {
315 AllocUnit * ptr = hashTable[i];
316 while(ptr) {
317 // skip external allocations
318 if(ptr->srcLine)
319 ++res;
320 ptr = ptr->next;
323 return res;
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 "-------- ------- ------- --------------------------------"
334 "-------------- \n";
336 for (uint i = 0; i < hashSize; ++i) {
337 AllocUnit * ptr = hashTable[i];
338 while(ptr) {
339 // skip external allocations
340 if(ptr->srcLine) {
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;
355 ptr = ptr->next;
360 void DumpLeakReport(void)
362 // Any leaks?
363 uint leaks = CalcLeaks();
364 if(leaks == 0) {
365 vLog << "No memory leaks found." << std::endl;
366 return;
369 // Header
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")
380 << " found:\n\n";
382 DumpAllocations(vLog);
383 vLog.flush();
386 void ResetGlobals(void)
388 sourceFile = "??";
389 sourceLine = 0;
390 sourceFunc = "??";
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, " ");
432 errorFlag = true;
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, " ");
447 errorFlag = true;
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)
460 return !errorFlag;
464 * Init functions
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 */
475 char tmpstr[40];
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;
512 return true;
515 void ReleaseMemoryManager(void)
517 #ifdef LOG_SINGLETONS
518 vLog << "Releasing memory manager." << std::endl;
519 #endif /* LOG_SINGLETONS */
521 DumpLeakReport();
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);
529 reservoirBuffer = 0;
530 reservoirBufferSize = 0;
531 reservoir = NULL;
533 // deinitialized
534 released = true;
538 * Config functions
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
550 alwaysLogAll = st;
553 void SetAlwaysWipeAll(bool st)
555 // Force this software to always wipe memory with a pattern when
556 // it is being allocated/dallocated
557 alwaysWipeAll = st;
560 void SetRandomWipe(bool st)
562 // Force this software to use a random pattern when wiping memory
563 // -- good for stress testing
564 randomWipe = true;
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;
606 * Used by macros
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
617 sourceFile = file;
618 sourceLine = line;
619 sourceFunc = func;
623 * Allocate memory and track it
625 void * Allocator(const char * file, unsigned line,
626 const char * func, AllocType type, size_t size)
628 try {
629 // requesting memory at the program cleanup?
630 BREAK_ASSERT(!released);
632 // Increase our allocation count
633 if(line)
634 ++currentAllocationCount;
636 // Log the request
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
653 if(!reservoir) {
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 *));
675 BREAK_ASSERT(temp);
676 if(temp) {
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
693 double a = rand();
694 double b = RAND_MAX / 100.0 * RANDOM_FAILURE;
695 if (a > b) {
696 au->actualAddress = malloc(au->actualSize);
697 } else {
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;
707 au->srcLine = line;
708 au->allocNumber = currentAllocationCount;
710 if(file)
711 strncpy(au->srcFile, SourceFileStripper(file),
712 sizeof(au->srcFile) - 1);
713 else
714 strcpy(au->srcFile, "??");
716 if(func)
717 strncpy(au->srcFunc, func, sizeof(au->srcFunc) - 1);
718 else
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];
745 au->prev = NULL;
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();
777 // Log the result
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.
787 ResetGlobals();
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;
795 ResetGlobals();
796 return NULL;
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)
806 try {
807 // Calling realloc with a NULL should force same operations as a malloc
808 if (!address) {
809 void * res = Allocator(file, line, func, type, size ? size : 1);
810 return res;
813 if(size == 0) {
814 Deallocator(file, line, func, atRealloc, address);
815 return NULL;
818 // requesting memory at the program cleanup?
819 BREAK_ASSERT(!released);
821 // Increase our allocation count
822 if(line)
823 ++currentAllocationCount;
825 // If you hit this assert, you requested a breakpoint
826 // on a specific allocation count
827 BREAK_ASSERT(currentAllocationCount != breakOnAllocationCount);
829 // Log the request
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);
846 if(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
886 double a = rand();
887 double b = RAND_MAX / 100.0 * RANDOM_FAILURE;
888 if (a > b) {
889 newActualAddress = realloc(au->actualAddress, newActualSize);
890 } else {
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;
924 au->srcLine = line;
925 au->allocNumber = currentAllocationCount;
927 if(file)
928 strncpy(au->srcFile, SourceFileStripper(file), sizeof(au->srcFile) - 1);
929 else
930 strcpy(au->srcFile, "??");
932 if(func)
933 strncpy(au->srcFunc, func, sizeof(au->srcFunc) - 1);
934 else
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;
945 } else {
946 if(au->prev)
947 au->prev->next = au->next;
948 if(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];
958 au->prev = NULL;
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();
991 // Log the result
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.
1002 ResetGlobals();
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;
1010 ResetGlobals();
1011 return NULL;
1016 * Deallocate memory and track it
1018 void Deallocator(const char * file, unsigned line,
1019 const char * func, AllocType type, const void * address)
1021 try {
1022 // determine the allocator, since stack may get confused
1023 AllocUnit * au = NULL;
1024 if(address)
1025 au = FindAllocUnit(address);
1027 // it's an external allocation
1028 if(!line && !au)
1029 return;
1031 // Log the request
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)
1039 << std::endl;
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.
1046 if(address) {
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);
1051 if(au == NULL)
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
1086 // we do. Oh well.
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;
1097 } else {
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;
1113 reservoir = au;
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.
1119 ResetGlobals();
1121 // Validate every single allocated unit in memory
1122 if (alwaysValidateAll)
1123 ValidateAllAllocUnits();
1124 } catch(const char *err) {
1125 // Deal with errors
1126 vLog << fac(lfMemory) << "[!] " << err << std::endl;
1127 ResetGlobals();
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];
1150 while(ptr) {
1151 ++allocCount;
1152 if(!ValidateAllocUnit(ptr))
1153 ++errors;
1154 ptr = ptr->next;
1158 // Test for hash-table correctness
1159 if(allocCount != stats.totalAllocUnitCount) {
1160 vLog << fac(lfMemory)
1161 << "[!] Memory tracking hash table corrupt!" << std::endl;
1162 ++errors;
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);
1180 // Log any errors
1181 if(errors)
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
1187 return errors != 0;
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
1199 unsigned total = 0;
1200 for (unsigned i = 0; i < hashSize; ++i) {
1201 AllocUnit * ptr = hashTable[i];
1202 while(ptr) {
1203 total += CalcUnused(ptr);
1204 ptr = ptr->next;
1207 return total;
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";
1225 // Report summary
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)
1280 return stats;
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
1298 if(size == 0)
1299 size = 1;
1301 // ANSI says: loop continuously because the error handler
1302 // could possibly free up some memory
1304 for(;;) {
1305 // Try the allocation
1306 void * ptr = tre::Allocator(file, line, func, tre::atNew, size);
1307 if(ptr) {
1308 return ptr;
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
1317 if(nh) {
1318 (*nh)();
1320 // Otherwise, throw the exception
1321 } else {
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
1336 if(size == 0)
1337 size = 1;
1339 // ANSI says: loop continuously because the error handler could
1340 // possibly free up some memory
1341 for(;;) {
1342 // Try the allocation
1343 void * ptr = tre::Allocator(file, line, func, tre::atNewArray, size);
1344 if(ptr) {
1345 return ptr;
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
1355 if(nh) {
1356 (*nh)();
1357 // Otherwise, throw the exception
1358 } else {
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
1376 if(size == 0)
1377 size = 1;
1379 // ANSI says: loop continuously because the error handler could
1380 // possibly free up some memory
1381 for(;;) {
1382 // Try the allocation
1383 void * ptr = tre::Allocator(file, line, "??", tre::atNew, size);
1384 if(ptr) {
1385 return ptr;
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
1394 if(nh) {
1395 (*nh)();
1396 // Otherwise, throw the exception
1397 } else {
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
1408 if(size == 0)
1409 size = 1;
1411 // ANSI says: loop continuously because the error handler could
1412 // possibly free up some memory
1413 for(;;) {
1414 // Try the allocation
1415 void * ptr = tre::Allocator(file, line, "??", tre::atNewArray, size);
1416 if(ptr) {
1417 return ptr;
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
1426 if(nh) {
1427 (*nh)();
1429 // Otherwise, throw the exception
1430 } else {
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)
1444 if(address)
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)
1462 if(address)
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]
1468 << " of NULL by "
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
1483 // on here.
1484 /// Make compiler happy!
1485 void operator delete(void * address, const char *, int) throw()
1487 free(address);
1490 /// Make compiler happy!
1491 void operator delete[](void * address, const char *, int) throw()
1493 free(address);
1496 #endif /* MEM_MANAGER_ON */