1
// ---------------------------------------------------------------------------------------------------------------------------------
4
// _ __ ___ _ __ ___ __ _ _ __ ___ _ __ _ __
5
// | '_ ` _ \| '_ ` _ \ / _` | '__| / __| '_ \| '_ \
6
// | | | | | | | | | | | (_| | | _ | (__| |_) | |_) |
7
// |_| |_| |_|_| |_| |_|\__, |_| (_) \___| .__/| .__/
11
// Memory manager & tracking software
13
// Best viewed with 8-character tabs and (at least) 132 columns
15
// ---------------------------------------------------------------------------------------------------------------------------------
17
// Restrictions & freedoms pertaining to usage and redistribution of this software:
19
// * This software is 100% free
20
// * If you use this software (in part or in whole) you must credit the author.
21
// * This software may not be re-distributed (in part or in whole) in a modified
22
// form without clear documentation on how to obtain a copy of the original work.
23
// * You may not use this software to directly or indirectly cause harm to others.
24
// * This software is provided as-is and without warrantee. Use at your own risk.
26
// For more information, visit HTTP://www.FluidStudios.com
28
// ---------------------------------------------------------------------------------------------------------------------------------
29
// Originally created on 12/22/2000 by Paul Nettle
31
// Copyright 2000, Fluid Studios, Inc., all rights reserved.
32
// ---------------------------------------------------------------------------------------------------------------------------------
36
// This software is self-documented with periodic comments. Before you start using this software, perform a search for the string
37
// "-DOC-" to locate pertinent information about how to use this software.
39
// You are also encouraged to read the comment blocks throughout this source file. They will help you understand how this memory
40
// tracking software works, so you can better utilize it within your applications.
44
// 1. This code purposely uses no external routines that allocate RAM (other than the raw allocation routines, such as malloc). We
45
// do this because we want this to be as self-contained as possible. As an example, we don't use assert, because when running
46
// under WIN32, the assert brings up a dialog box, which allocates RAM. Doing this in the middle of an allocation would be bad.
48
// 2. When trying to override new/delete under MFC (which has its own version of global new/delete) the linker will complain. In
49
// order to fix this error, use the compiler option: /FORCE, which will force it to build an executable even with linker errors.
50
// Be sure to check those errors each time you compile, otherwise, you may miss a valid linker error.
52
// 3. If you see something that looks odd to you or seems like a strange way of going about doing something, then consider that this
53
// code was carefully thought out. If something looks odd, then just assume I've got a good reason for doing it that way (an
54
// example is the use of the class MemStaticTimeTracker.)
56
// 4. With MFC applications, you will need to comment out any occurance of "#define new DEBUG_NEW" from all source files.
58
// 5. Include file dependencies are _very_important_ for getting the MMGR to integrate nicely into your application. Be careful if
59
// you're including standard includes from within your own project inclues; that will break this very specific dependency order.
60
// It should look like this:
62
// #include <stdio.h> // Standard includes MUST come first
63
// #include <stdlib.h> //
64
// #include <streamio> //
66
// #include "mmgr.h" // mmgr.h MUST come next
68
// #include "myfile1.h" // Project includes MUST come last
69
// #include "myfile2.h" //
70
// #include "myfile3.h" //
72
// ---------------------------------------------------------------------------------------------------------------------------------
90
// ---------------------------------------------------------------------------------------------------------------------------------
91
// -DOC- If you're like me, it's hard to gain trust in foreign code. This memory manager will try to INDUCE your code to crash (for
92
// very good reasons... like making bugs obvious as early as possible.) Some people may be inclined to remove this memory tracking
93
// software if it causes crashes that didn't exist previously. In reality, these new crashes are the BEST reason for using this
96
// Whether this software causes your application to crash, or if it reports errors, you need to be able to TRUST this software. To
97
// this end, you are given some very simple debugging tools.
99
// The quickest way to locate problems is to enable the STRESS_TEST macro (below.) This should catch 95% of the crashes before they
100
// occur by validating every allocation each time this memory manager performs an allocation function. If that doesn't work, keep
103
// If you enable the TEST_MEMORY_MANAGER #define (below), this memory manager will log an entry in the memory.log file each time it
104
// enters and exits one of its primary allocation handling routines. Each call that succeeds should place an "ENTER" and an "EXIT"
105
// into the log. If the program crashes within the memory manager, it will log an "ENTER", but not an "EXIT". The log will also
106
// report the name of the routine.
108
// Just because this memory manager crashes does not mean that there is a bug here! First, an application could inadvertantly damage
109
// the heap, causing malloc(), realloc() or free() to crash. Also, an application could inadvertantly damage some of the memory used
110
// by this memory tracking software, causing it to crash in much the same way that a damaged heap would affect the standard
111
// allocation routines.
113
// In the event of a crash within this code, the first thing you'll want to do is to locate the actual line of code that is
114
// crashing. You can do this by adding log() entries throughout the routine that crashes, repeating this process until you narrow
115
// in on the offending line of code. If the crash happens in a standard C allocation routine (i.e. malloc, realloc or free) don't
116
// bother contacting me, your application has damaged the heap. You can help find the culprit in your code by enabling the
117
// STRESS_TEST macro (below.)
119
// If you truely suspect a bug in this memory manager (and you had better be sure about it! :) you can contact me at
120
// midnight@FluidStudios.com. Before you do, however, check for a newer version at:
122
// http://www.FluidStudios.com/publications.html
124
// When using this debugging aid, make sure that you are NOT setting the alwaysLogAll variable on, otherwise the log could be
125
// cluttered and hard to read.
126
// ---------------------------------------------------------------------------------------------------------------------------------
128
//#define TEST_MEMORY_MANAGER
130
// ---------------------------------------------------------------------------------------------------------------------------------
131
// -DOC- Enable this sucker if you really want to stress-test your app's memory usage, or to help find hard-to-find bugs
132
// ---------------------------------------------------------------------------------------------------------------------------------
136
// ---------------------------------------------------------------------------------------------------------------------------------
137
// -DOC- Enable this sucker if you want to stress-test your app's error-handling. Set RANDOM_FAIL to the percentage of failures you
138
// want to test with (0 = none, >100 = all failures).
139
// ---------------------------------------------------------------------------------------------------------------------------------
141
//#define RANDOM_FAILURE 10.0
143
// ---------------------------------------------------------------------------------------------------------------------------------
144
// -DOC- Locals -- modify these flags to suit your needs
145
// ---------------------------------------------------------------------------------------------------------------------------------
148
static const unsigned int hashBits = 12;
149
static bool randomWipe = true;
150
static bool alwaysValidateAll = true;
151
static bool alwaysLogAll = true;
152
static bool alwaysWipeAll = true;
153
static bool cleanupLogOnFirstRun = true;
154
static const unsigned int paddingSize = 1024; // An extra 8K per allocation!
156
static const unsigned int hashBits = 12;
157
static bool randomWipe = false;
158
static bool alwaysValidateAll = false;
159
static bool alwaysLogAll = false;
160
static bool alwaysWipeAll = true;
161
static bool cleanupLogOnFirstRun = true;
162
static const unsigned int paddingSize = 4;
165
// ---------------------------------------------------------------------------------------------------------------------------------
166
// We define our own assert, because we don't want to bring up an assertion dialog, since that allocates RAM. Our new assert
167
// simply declares a forced breakpoint.
169
// The BEOS assert added by Arvid Norberg <arvid@iname.com>.
170
// ---------------------------------------------------------------------------------------------------------------------------------
174
#define m_assert(x) if ((x) == false) __asm { int 3 }
176
#define m_assert(x) {}
178
#elif defined(__BEOS__)
180
extern void debugger(const char *message);
181
#define m_assert(x) if ((x) == false) debugger("mmgr: assert failed")
183
#define m_assert(x) {}
185
#else // Linux uses assert, which we can use safely, since it doesn't bring up a dialog within the program.
186
#define m_assert(cond) assert(cond)
189
// ---------------------------------------------------------------------------------------------------------------------------------
190
// Here, we turn off our macros because any place in this source file where the word 'new' or the word 'delete' (etc.)
191
// appear will be expanded by the macro. So to avoid problems using them within this source file, we'll just #undef them.
192
// ---------------------------------------------------------------------------------------------------------------------------------
201
// ---------------------------------------------------------------------------------------------------------------------------------
202
// Defaults for the constants & statics in the MemoryManager class
203
// ---------------------------------------------------------------------------------------------------------------------------------
205
const unsigned int m_alloc_unknown = 0;
206
const unsigned int m_alloc_new = 1;
207
const unsigned int m_alloc_new_array = 2;
208
const unsigned int m_alloc_malloc = 3;
209
const unsigned int m_alloc_calloc = 4;
210
const unsigned int m_alloc_realloc = 5;
211
const unsigned int m_alloc_delete = 6;
212
const unsigned int m_alloc_delete_array = 7;
213
const unsigned int m_alloc_free = 8;
215
// ---------------------------------------------------------------------------------------------------------------------------------
216
// -DOC- Get to know these values. They represent the values that will be used to fill unused and deallocated RAM.
217
// ---------------------------------------------------------------------------------------------------------------------------------
219
static unsigned int prefixPattern = 0xbaadf00d; // Fill pattern for bytes preceeding allocated blocks
220
static unsigned int postfixPattern = 0xdeadc0de; // Fill pattern for bytes following allocated blocks
221
static unsigned int unusedPattern = 0xfeedface; // Fill pattern for freshly allocated blocks
222
static unsigned int releasedPattern = 0xdeadbeef; // Fill pattern for deallocated blocks
224
// ---------------------------------------------------------------------------------------------------------------------------------
226
// ---------------------------------------------------------------------------------------------------------------------------------
228
static const unsigned int hashSize = 1 << hashBits;
229
static const char *allocationTypes[] = {"Unknown",
230
"new", "new[]", "malloc", "calloc",
231
"realloc", "delete", "delete[]", "free"};
232
static sAllocUnit *hashTable[hashSize];
233
static sAllocUnit *reservoir;
234
static unsigned int currentAllocationCount = 0;
235
static unsigned int breakOnAllocationCount = 0;
236
static sMStats stats;
237
static const char *sourceFile = "??";
238
static const char *sourceFunc = "??";
239
static unsigned int sourceLine = 0;
240
static bool staticDeinitTime = false;
241
static sAllocUnit **reservoirBuffer = NULL;
242
static unsigned int reservoirBufferSize = 0;
243
static const char *memoryLogFile = "memory.log";
244
static const char *memoryLeakLogFile = "memleaks.log";
245
static void doCleanupLogOnFirstRun();
247
// ---------------------------------------------------------------------------------------------------------------------------------
248
// Local functions only
249
// ---------------------------------------------------------------------------------------------------------------------------------
251
static void log(const char *format, ...)
255
static char buffer[2048];
257
va_start(ap, format);
258
vsprintf(buffer, format, ap);
263
if (cleanupLogOnFirstRun) doCleanupLogOnFirstRun();
267
FILE *fp = fopen(memoryLogFile, "ab");
269
// If you hit this assert, then the memory logger is unable to log information to a file (can't open the file for some
270
// reason.) You can interrogate the variable 'buffer' to see what was supposed to be logged (but won't be.)
275
// Spit out the data to the log
277
fprintf(fp, "%s\r\n", buffer);
281
// ---------------------------------------------------------------------------------------------------------------------------------
283
static void doCleanupLogOnFirstRun()
285
if (cleanupLogOnFirstRun)
287
unlink(memoryLogFile);
288
cleanupLogOnFirstRun = false;
290
// Print a header for the log
292
time_t t = time(NULL);
293
log("--------------------------------------------------------------------------------");
295
log(" %s - Memory logging file created on %s", memoryLogFile, asctime(localtime(&t)));
296
log("--------------------------------------------------------------------------------");
298
log("This file contains a log of all memory operations performed during the last run.");
300
log("Interrogate this file to track errors or to help track down memory-related");
301
log("issues. You can do this by tracing the allocations performed by a specific owner");
302
log("or by tracking a specific address through a series of allocations and");
303
log("reallocations.");
305
log("There is a lot of useful information here which, when used creatively, can be");
306
log("extremely helpful.");
308
log("Note that the following guides are used throughout this file:");
311
log(" [+] - Allocation");
312
log(" [~] - Reallocation");
313
log(" [-] - Deallocation");
314
log(" [I] - Generic information");
315
log(" [F] - Failure induced for the purpose of stress-testing your application");
316
log(" [D] - Information used for debugging this memory manager");
318
log("...so, to find all errors in the file, search for \"[!]\"");
320
log("--------------------------------------------------------------------------------");
324
// ---------------------------------------------------------------------------------------------------------------------------------
326
static const char *sourceFileStripper(const char *sourceFile)
328
char *ptr = strrchr(sourceFile, '\\');
329
if (ptr) return ptr + 1;
330
ptr = strrchr(sourceFile, '/');
331
if (ptr) return ptr + 1;
335
// ---------------------------------------------------------------------------------------------------------------------------------
337
static const char *ownerString(const char *sourceFile, const unsigned int sourceLine, const char *sourceFunc)
340
memset(str, 0, sizeof(str));
341
sprintf(str, "%s(%05d)::%s", sourceFileStripper(sourceFile), sourceLine, sourceFunc);
345
// ---------------------------------------------------------------------------------------------------------------------------------
347
static const char *insertCommas(unsigned int value)
350
memset(str, 0, sizeof(str));
352
sprintf(str, "%u", value);
355
memmove(&str[strlen(str)-3], &str[strlen(str)-4], 4);
356
str[strlen(str) - 4] = ',';
360
memmove(&str[strlen(str)-7], &str[strlen(str)-8], 8);
361
str[strlen(str) - 8] = ',';
363
if (strlen(str) > 11)
365
memmove(&str[strlen(str)-11], &str[strlen(str)-12], 12);
366
str[strlen(str) - 12] = ',';
372
// ---------------------------------------------------------------------------------------------------------------------------------
374
static const char *memorySizeString(unsigned long size)
377
if (size > (1024*1024)) sprintf(str, "%10s (%7.2fM)", insertCommas(size), (float) size / (1024.0f * 1024.0f));
378
else if (size > 1024) sprintf(str, "%10s (%7.2fK)", insertCommas(size), (float) size / 1024.0f);
379
else sprintf(str, "%10s bytes ", insertCommas(size));
383
// ---------------------------------------------------------------------------------------------------------------------------------
385
static sAllocUnit *findAllocUnit(const void *reportedAddress)
388
m_assert(reportedAddress != NULL);
390
// Use the address to locate the hash index. Note that we shift off the lower four bits. This is because most allocated
391
// addresses will be on four-, eight- or even sixteen-byte boundaries. If we didn't do this, the hash index would not have
392
// very good coverage.
394
unsigned int hashIndex = ((unsigned int) reportedAddress >> 4) & (hashSize - 1);
395
sAllocUnit *ptr = hashTable[hashIndex];
398
if (ptr->reportedAddress == reportedAddress) return ptr;
405
// ---------------------------------------------------------------------------------------------------------------------------------
407
static size_t calculateActualSize(const size_t reportedSize)
409
// We use DWORDS as our padding, and a long is guaranteed to be 4 bytes, but an int is not (ANSI defines an int as
410
// being the standard word size for a processor; on a 32-bit machine, that's 4 bytes, but on a 64-bit machine, it's
411
// 8 bytes, which means an int can actually be larger than a long.)
413
return reportedSize + paddingSize * sizeof(long) * 2;
416
// ---------------------------------------------------------------------------------------------------------------------------------
418
static size_t calculateReportedSize(const size_t actualSize)
420
// We use DWORDS as our padding, and a long is guaranteed to be 4 bytes, but an int is not (ANSI defines an int as
421
// being the standard word size for a processor; on a 32-bit machine, that's 4 bytes, but on a 64-bit machine, it's
422
// 8 bytes, which means an int can actually be larger than a long.)
424
return actualSize - paddingSize * sizeof(long) * 2;
427
// ---------------------------------------------------------------------------------------------------------------------------------
429
static void *calculateReportedAddress(const void *actualAddress)
433
if (!actualAddress) return NULL;
435
// JUst account for the padding
437
return (void *) ((char *) actualAddress + sizeof(long) * paddingSize);
440
// ---------------------------------------------------------------------------------------------------------------------------------
442
static void wipeWithPattern(sAllocUnit *allocUnit, unsigned long pattern, const unsigned int originalReportedSize = 0)
444
// For a serious test run, we use wipes of random a random value. However, if this causes a crash, we don't want it to
445
// crash in a differnt place each time, so we specifically DO NOT call srand. If, by chance your program calls srand(),
446
// you may wish to disable that when running with a random wipe test. This will make any crashes more consistent so they
447
// can be tracked down easier.
451
pattern = ((rand() & 0xff) << 24) | ((rand() & 0xff) << 16) | ((rand() & 0xff) << 8) | (rand() & 0xff);
454
// -DOC- We should wipe with 0's if we're not in debug mode, so we can help hide bugs if possible when we release the
455
// product. So uncomment the following line for releases.
457
// Note that the "alwaysWipeAll" should be turned on for this to have effect, otherwise it won't do much good. But we'll
458
// leave it this way (as an option) because this does slow things down.
461
// This part of the operation is optional
463
if (alwaysWipeAll && allocUnit->reportedSize > originalReportedSize)
467
long *lptr = (long *) ((char *)allocUnit->reportedAddress + originalReportedSize);
468
int length = allocUnit->reportedSize - originalReportedSize;
470
for (i = 0; i < (length >> 2); i++, lptr++)
475
// Fill the remainder
477
unsigned int shiftCount = 0;
478
char *cptr = (char *) lptr;
479
for (i = 0; i < (length & 0x3); i++, cptr++, shiftCount += 8)
481
*cptr = (pattern & (0xff << shiftCount)) >> shiftCount;
485
// Write in the prefix/postfix bytes
487
long *pre = (long *) allocUnit->actualAddress;
488
long *post = (long *) ((char *)allocUnit->actualAddress + allocUnit->actualSize - paddingSize * sizeof(long));
489
for (unsigned int i = 0; i < paddingSize; i++, pre++, post++)
491
*pre = prefixPattern;
492
*post = postfixPattern;
496
// ---------------------------------------------------------------------------------------------------------------------------------
498
static void dumpAllocations(FILE *fp)
500
fprintf(fp, "Alloc. Addr Size Addr Size BreakOn BreakOn \r\n");
501
fprintf(fp, "Number Reported Reported Actual Actual Unused Method Dealloc Realloc Allocated by \r\n");
502
fprintf(fp, "------ ---------- ---------- ---------- ---------- ---------- -------- ------- ------- --------------------------------------------------- \r\n");
505
for (unsigned int i = 0; i < hashSize; i++)
507
sAllocUnit *ptr = hashTable[i];
510
fprintf(fp, "%06d 0x%08X 0x%08X 0x%08X 0x%08X 0x%08X %-8s %c %c %s\r\n",
511
ptr->allocationNumber,
512
(unsigned int) ptr->reportedAddress, ptr->reportedSize,
513
(unsigned int) ptr->actualAddress, ptr->actualSize,
515
allocationTypes[ptr->allocationType],
516
ptr->breakOnDealloc ? 'Y':'N',
517
ptr->breakOnRealloc ? 'Y':'N',
518
ownerString(ptr->sourceFile, ptr->sourceLine, ptr->sourceFunc));
524
// ---------------------------------------------------------------------------------------------------------------------------------
526
static void dumpLeakReport()
528
// Open the report file
530
FILE *fp = fopen(memoryLeakLogFile, "w+b");
532
// If you hit this assert, then the memory report generator is unable to log information to a file (can't open the file for
541
static char timeString[25];
542
memset(timeString, 0, sizeof(timeString));
543
time_t t = time(NULL);
544
struct tm *tme = localtime(&t);
545
fprintf(fp, " ---------------------------------------------------------------------------------------------------------------------------------- \r\n");
546
fprintf(fp, "| Memory leak report for: %02d/%02d/%04d %02d:%02d:%02d |\r\n", tme->tm_mon + 1, tme->tm_mday, tme->tm_year + 1900, tme->tm_hour, tme->tm_min, tme->tm_sec);
547
fprintf(fp, " ---------------------------------------------------------------------------------------------------------------------------------- \r\n");
550
if (stats.totalAllocUnitCount)
552
fprintf(fp, "%d memory leak%s found:\r\n", stats.totalAllocUnitCount, stats.totalAllocUnitCount == 1 ? "":"s");
556
fprintf(fp, "Congratulations! No memory leaks found!\r\n");
558
// We can finally free up our own memory allocations
562
for (unsigned int i = 0; i < reservoirBufferSize; i++)
564
free(reservoirBuffer[i]);
566
free(reservoirBuffer);
568
reservoirBufferSize = 0;
574
if (stats.totalAllocUnitCount)
582
// ---------------------------------------------------------------------------------------------------------------------------------
583
// We use a static class to let us know when we're in the midst of static deinitialization
584
// ---------------------------------------------------------------------------------------------------------------------------------
586
class MemStaticTimeTracker
589
MemStaticTimeTracker() {doCleanupLogOnFirstRun();}
590
~MemStaticTimeTracker() {staticDeinitTime = true; dumpLeakReport();}
592
static MemStaticTimeTracker mstt;
594
// ---------------------------------------------------------------------------------------------------------------------------------
595
// -DOC- Flags & options -- Call these routines to enable/disable the following options
596
// ---------------------------------------------------------------------------------------------------------------------------------
598
bool &m_alwaysValidateAll()
600
// Force a validation of all allocation units each time we enter this software
601
return alwaysValidateAll;
604
// ---------------------------------------------------------------------------------------------------------------------------------
606
bool &m_alwaysLogAll()
608
// Force a log of every allocation & deallocation into memory.log
612
// ---------------------------------------------------------------------------------------------------------------------------------
614
bool &m_alwaysWipeAll()
616
// Force this software to always wipe memory with a pattern when it is being allocated/dallocated
617
return alwaysWipeAll;
620
// ---------------------------------------------------------------------------------------------------------------------------------
622
bool &m_randomeWipe()
624
// Force this software to use a random pattern when wiping memory -- good for stress testing
628
// ---------------------------------------------------------------------------------------------------------------------------------
629
// -DOC- Simply call this routine with the address of an allocated block of RAM, to cause it to force a breakpoint when it is
631
// ---------------------------------------------------------------------------------------------------------------------------------
633
bool &m_breakOnRealloc(void *reportedAddress)
635
// Locate the existing allocation unit
637
sAllocUnit *au = findAllocUnit(reportedAddress);
639
// If you hit this assert, you tried to set a breakpoint on reallocation for an address that doesn't exist. Interrogate the
640
// stack frame or the variable 'au' to see which allocation this is.
641
m_assert(au != NULL);
643
// If you hit this assert, you tried to set a breakpoint on reallocation for an address that wasn't allocated in a way that
644
// is compatible with reallocation.
645
m_assert(au->allocationType == m_alloc_malloc ||
646
au->allocationType == m_alloc_calloc ||
647
au->allocationType == m_alloc_realloc);
649
return au->breakOnRealloc;
652
// ---------------------------------------------------------------------------------------------------------------------------------
653
// -DOC- Simply call this routine with the address of an allocated block of RAM, to cause it to force a breakpoint when it is
655
// ---------------------------------------------------------------------------------------------------------------------------------
657
bool &m_breakOnDealloc(void *reportedAddress)
659
// Locate the existing allocation unit
661
sAllocUnit *au = findAllocUnit(reportedAddress);
663
// If you hit this assert, you tried to set a breakpoint on deallocation for an address that doesn't exist. Interrogate the
664
// stack frame or the variable 'au' to see which allocation this is.
665
m_assert(au != NULL);
667
return au->breakOnDealloc;
670
// ---------------------------------------------------------------------------------------------------------------------------------
671
// -DOC- When tracking down a difficult bug, use this routine to force a breakpoint on a specific allocation count
672
// ---------------------------------------------------------------------------------------------------------------------------------
674
void m_breakOnAllocation(unsigned int count)
676
breakOnAllocationCount = count;
679
// ---------------------------------------------------------------------------------------------------------------------------------
680
// Used by the macros
681
// ---------------------------------------------------------------------------------------------------------------------------------
683
void m_setOwner(const char *file, const unsigned int line, const char *func)
685
// You're probably wondering about this...
687
// It's important for this memory manager to primarily work with global new/delete in their original forms (i.e. with
688
// no extra parameters.) In order to do this, we use macros that call this function prior to operators new & delete. This
689
// is fine... usually. Here's what actually happens when you use this macro to delete an object:
691
// m_setOwner(__FILE__, __LINE__, __FUNCTION__) --> object::~object() --> delete
693
// Note that the compiler inserts a call to the object's destructor just prior to calling our overridden operator delete.
694
// But what happens when we delete an object whose destructor deletes another object, whose desctuctor deletes another
695
// object? Here's a diagram (indentation follows stack depth):
697
// m_setOwner(...) -> ~obj1() // original call to delete obj1
698
// m_setOwner(...) -> ~obj2() // obj1's destructor deletes obj2
699
// m_setOwner(...) -> ~obj3() // obj2's destructor deletes obj3
700
// ... // obj3's destructor just does some stuff
701
// delete // back in obj2's destructor, we call delete
702
// delete // back in obj1's destructor, we call delete
703
// delete // back to our original call, we call delete
705
// Because m_setOwner() just sets up some static variables (below) it's important that each call to m_setOwner() and
706
// successive calls to new/delete alternate. However, in this case, three calls to m_setOwner() happen in succession
707
// followed by three calls to delete in succession (with a few calls to destructors mixed in for fun.) This means that
708
// only the final call to delete (in this chain of events) will have the proper reporting, and the first two in the chain
709
// will not have ANY owner-reporting information. The deletes will still work fine, we just won't know who called us.
711
// "Then build a stack, my friend!" you might think... but it's a very common thing that people will be working with third-
712
// party libraries (including MFC under Windows) which is not compiled with this memory manager's macros. In those cases,
713
// m_setOwner() is never called, and rightfully should not have the proper trace-back information. So if one of the
714
// destructors in the chain ends up being a call to a delete from a non-mmgr-compiled library, the stack will get confused.
716
// I've been unable to find a solution to this problem, but at least we can detect it and report the data before we
717
// lose it. That's what this is all about. It makes it somewhat confusing to read in the logs, but at least ALL the
718
// information is present...
720
// There's a caveat here... The compiler is not required to call operator delete if the value being deleted is NULL.
721
// In this case, any call to delete with a NULL will sill call m_setOwner(), which will make m_setOwner() think that
722
// there is a destructor chain becuase we setup the variables, but nothing gets called to clear them. Because of this
723
// we report a "Possible destructor chain".
725
// Thanks to J. Woznack (from Kodiak Interactive Software Studios -- www.kodiakgames.com) for pointing this out.
727
if (sourceLine && alwaysLogAll)
729
log("[I] NOTE! Possible destructor chain: previous owner is %s", ownerString(sourceFile, sourceLine, sourceFunc));
732
// Okay... save this stuff off so we can keep track of the caller
739
// ---------------------------------------------------------------------------------------------------------------------------------
741
static void resetGlobals()
748
// ---------------------------------------------------------------------------------------------------------------------------------
751
// These are the standard new/new[] operators. They are merely interface functions that operate like normal new/new[], but use our
752
// memory tracking routines.
753
// ---------------------------------------------------------------------------------------------------------------------------------
755
void *operator new(size_t reportedSize)
757
#ifdef TEST_MEMORY_MANAGER
758
log("[D] ENTER: new");
763
const char *file = sourceFile;
764
const unsigned int line = sourceLine;
765
const char *func = sourceFunc;
767
// ANSI says: allocation requests of 0 bytes will still return a valid value
769
if (reportedSize == 0) reportedSize = 1;
771
// ANSI says: loop continuously because the error handler could possibly free up some memory
775
// Try the allocation
777
void *ptr = m_allocator(file, line, func, m_alloc_new, reportedSize);
780
#ifdef TEST_MEMORY_MANAGER
781
log("[D] EXIT : new");
786
// There isn't a way to determine the new handler, except through setting it. So we'll just set it to NULL, then
787
// set it back again.
789
std::new_handler nh = std::set_new_handler(0);
790
std::set_new_handler(nh);
792
// If there is an error handler, call it
799
// Otherwise, throw the exception
803
#ifdef TEST_MEMORY_MANAGER
804
log("[D] EXIT : new");
806
throw std::bad_alloc();
811
// ---------------------------------------------------------------------------------------------------------------------------------
813
void *operator new[](size_t reportedSize)
815
#ifdef TEST_MEMORY_MANAGER
816
log("[D] ENTER: new[]");
821
const char *file = sourceFile;
822
const unsigned int line = sourceLine;
823
const char *func = sourceFunc;
825
// The ANSI standard says that allocation requests of 0 bytes will still return a valid value
827
if (reportedSize == 0) reportedSize = 1;
829
// ANSI says: loop continuously because the error handler could possibly free up some memory
833
// Try the allocation
835
void *ptr = m_allocator(file, line, func, m_alloc_new_array, reportedSize);
838
#ifdef TEST_MEMORY_MANAGER
839
log("[D] EXIT : new[]");
844
// There isn't a way to determine the new handler, except through setting it. So we'll just set it to NULL, then
845
// set it back again.
847
std::new_handler nh = std::set_new_handler(0);
848
std::set_new_handler(nh);
850
// If there is an error handler, call it
857
// Otherwise, throw the exception
861
#ifdef TEST_MEMORY_MANAGER
862
log("[D] EXIT : new[]");
864
throw std::bad_alloc();
869
// ---------------------------------------------------------------------------------------------------------------------------------
870
// Other global new/new[]
872
// These are the standard new/new[] operators as used by Microsoft's memory tracker. We don't want them interfering with our memory
873
// tracking efforts. Like the previous versions, these are merely interface functions that operate like normal new/new[], but use
874
// our memory tracking routines.
875
// ---------------------------------------------------------------------------------------------------------------------------------
877
void *operator new(size_t reportedSize, const char *sourceFile, int sourceLine)
879
#ifdef TEST_MEMORY_MANAGER
880
log("[D] ENTER: new");
883
// The ANSI standard says that allocation requests of 0 bytes will still return a valid value
885
if (reportedSize == 0) reportedSize = 1;
887
// ANSI says: loop continuously because the error handler could possibly free up some memory
891
// Try the allocation
893
void *ptr = m_allocator(sourceFile, sourceLine, "??", m_alloc_new, reportedSize);
896
#ifdef TEST_MEMORY_MANAGER
897
log("[D] EXIT : new");
902
// There isn't a way to determine the new handler, except through setting it. So we'll just set it to NULL, then
903
// set it back again.
905
std::new_handler nh = std::set_new_handler(0);
906
std::set_new_handler(nh);
908
// If there is an error handler, call it
915
// Otherwise, throw the exception
919
#ifdef TEST_MEMORY_MANAGER
920
log("[D] EXIT : new");
922
throw std::bad_alloc();
927
// ---------------------------------------------------------------------------------------------------------------------------------
929
void *operator new[](size_t reportedSize, const char *sourceFile, int sourceLine)
931
#ifdef TEST_MEMORY_MANAGER
932
log("[D] ENTER: new[]");
935
// The ANSI standard says that allocation requests of 0 bytes will still return a valid value
937
if (reportedSize == 0) reportedSize = 1;
939
// ANSI says: loop continuously because the error handler could possibly free up some memory
943
// Try the allocation
945
void *ptr = m_allocator(sourceFile, sourceLine, "??", m_alloc_new_array, reportedSize);
948
#ifdef TEST_MEMORY_MANAGER
949
log("[D] EXIT : new[]");
954
// There isn't a way to determine the new handler, except through setting it. So we'll just set it to NULL, then
955
// set it back again.
957
std::new_handler nh = std::set_new_handler(0);
958
std::set_new_handler(nh);
960
// If there is an error handler, call it
967
// Otherwise, throw the exception
971
#ifdef TEST_MEMORY_MANAGER
972
log("[D] EXIT : new[]");
974
throw std::bad_alloc();
979
// ---------------------------------------------------------------------------------------------------------------------------------
980
// Global delete/delete[]
982
// These are the standard delete/delete[] operators. They are merely interface functions that operate like normal delete/delete[],
983
// but use our memory tracking routines.
984
// ---------------------------------------------------------------------------------------------------------------------------------
986
void operator delete(void *reportedAddress)
988
#ifdef TEST_MEMORY_MANAGER
989
log("[D] ENTER: delete");
992
// ANSI says: delete & delete[] allow NULL pointers (they do nothing)
994
if (reportedAddress) m_deallocator(sourceFile, sourceLine, sourceFunc, m_alloc_delete, reportedAddress);
995
else if (alwaysLogAll) log("[-] ----- %8s of NULL by %s", allocationTypes[m_alloc_delete], ownerString(sourceFile, sourceLine, sourceFunc));
997
// Resetting the globals insures that if at some later time, somebody calls our memory manager from an unknown
998
// source (i.e. they didn't include our H file) then we won't think it was the last allocation.
1002
#ifdef TEST_MEMORY_MANAGER
1003
log("[D] EXIT : delete");
1007
// ---------------------------------------------------------------------------------------------------------------------------------
1009
void operator delete[](void *reportedAddress)
1011
#ifdef TEST_MEMORY_MANAGER
1012
log("[D] ENTER: delete[]");
1015
// ANSI says: delete & delete[] allow NULL pointers (they do nothing)
1017
if (reportedAddress) m_deallocator(sourceFile, sourceLine, sourceFunc, m_alloc_delete_array, reportedAddress);
1018
else if (alwaysLogAll)
1019
log("[-] ----- %8s of NULL by %s", allocationTypes[m_alloc_delete_array], ownerString(sourceFile, sourceLine, sourceFunc));
1021
// Resetting the globals insures that if at some later time, somebody calls our memory manager from an unknown
1022
// source (i.e. they didn't include our H file) then we won't think it was the last allocation.
1026
#ifdef TEST_MEMORY_MANAGER
1027
log("[D] EXIT : delete[]");
1031
// ---------------------------------------------------------------------------------------------------------------------------------
1032
// Allocate memory and track it
1033
// ---------------------------------------------------------------------------------------------------------------------------------
1035
void *m_allocator(const char *sourceFile, const unsigned int sourceLine, const char *sourceFunc, const unsigned int allocationType, const size_t reportedSize)
1039
#ifdef TEST_MEMORY_MANAGER
1040
log("[D] ENTER: m_allocator()");
1043
// Increase our allocation count
1045
currentAllocationCount++;
1049
if (alwaysLogAll) log("[+] %05d %8s of size 0x%08X(%08d) by %s", currentAllocationCount, allocationTypes[allocationType], reportedSize, reportedSize, ownerString(sourceFile, sourceLine, sourceFunc));
1051
// If you hit this assert, you requested a breakpoint on a specific allocation count
1052
m_assert(currentAllocationCount != breakOnAllocationCount);
1054
// If necessary, grow the reservoir of unused allocation units
1058
// Allocate 256 reservoir elements
1060
reservoir = (sAllocUnit *) malloc(sizeof(sAllocUnit) * 256);
1062
// If you hit this assert, then the memory manager failed to allocate internal memory for tracking the
1064
m_assert(reservoir != NULL);
1066
// Danger Will Robinson!
1068
if (reservoir == NULL) throw "Unable to allocate RAM for internal memory tracking data";
1070
// Build a linked-list of the elements in our reservoir
1072
memset(reservoir, 0, sizeof(sAllocUnit) * 256);
1073
for (unsigned int i = 0; i < 256 - 1; i++)
1075
reservoir[i].next = &reservoir[i+1];
1078
// Add this address to our reservoirBuffer so we can free it later
1080
sAllocUnit **temp = (sAllocUnit **) realloc(reservoirBuffer, (reservoirBufferSize + 1) * sizeof(sAllocUnit *));
1084
reservoirBuffer = temp;
1085
reservoirBuffer[reservoirBufferSize++] = reservoir;
1089
// Logical flow says this should never happen...
1090
m_assert(reservoir != NULL);
1092
// Grab a new allocaton unit from the front of the reservoir
1094
sAllocUnit *au = reservoir;
1095
reservoir = au->next;
1097
// Populate it with some real data
1099
memset(au, 0, sizeof(sAllocUnit));
1100
au->actualSize = calculateActualSize(reportedSize);
1101
#ifdef RANDOM_FAILURE
1103
double b = RAND_MAX / 100.0 * RANDOM_FAILURE;
1106
au->actualAddress = malloc(au->actualSize);
1110
log("[F] Random faiure");
1111
au->actualAddress = NULL;
1114
au->actualAddress = malloc(au->actualSize);
1116
au->reportedSize = reportedSize;
1117
au->reportedAddress = calculateReportedAddress(au->actualAddress);
1118
au->allocationType = allocationType;
1119
au->sourceLine = sourceLine;
1120
au->allocationNumber = currentAllocationCount;
1121
if (sourceFile) strncpy(au->sourceFile, sourceFileStripper(sourceFile), sizeof(au->sourceFile) - 1);
1122
else strcpy (au->sourceFile, "??");
1123
if (sourceFunc) strncpy(au->sourceFunc, sourceFunc, sizeof(au->sourceFunc) - 1);
1124
else strcpy (au->sourceFunc, "??");
1126
// We don't want to assert with random failures, because we want the application to deal with them.
1128
#ifndef RANDOM_FAILURE
1129
// If you hit this assert, then the requested allocation simply failed (you're out of memory.) Interrogate the
1130
// variable 'au' or the stack frame to see what you were trying to do.
1131
m_assert(au->actualAddress != NULL);
1134
if (au->actualAddress == NULL)
1136
throw "Request for allocation failed. Out of memory.";
1139
// If you hit this assert, then this allocation was made from a source that isn't setup to use this memory tracking
1140
// software, use the stack frame to locate the source and include our H file.
1141
m_assert(allocationType != m_alloc_unknown);
1143
// Insert the new allocation into the hash table
1145
unsigned int hashIndex = ((unsigned int) au->reportedAddress >> 4) & (hashSize - 1);
1146
if (hashTable[hashIndex]) hashTable[hashIndex]->prev = au;
1147
au->next = hashTable[hashIndex];
1149
hashTable[hashIndex] = au;
1151
// Account for the new allocatin unit in our stats
1153
stats.totalReportedMemory += au->reportedSize;
1154
stats.totalActualMemory += au->actualSize;
1155
stats.totalAllocUnitCount++;
1156
if (stats.totalReportedMemory > stats.peakReportedMemory) stats.peakReportedMemory = stats.totalReportedMemory;
1157
if (stats.totalActualMemory > stats.peakActualMemory) stats.peakActualMemory = stats.totalActualMemory;
1158
if (stats.totalAllocUnitCount > stats.peakAllocUnitCount) stats.peakAllocUnitCount = stats.totalAllocUnitCount;
1159
stats.accumulatedReportedMemory += au->reportedSize;
1160
stats.accumulatedActualMemory += au->actualSize;
1161
stats.accumulatedAllocUnitCount++;
1163
// Prepare the allocation unit for use (wipe it with recognizable garbage)
1165
wipeWithPattern(au, unusedPattern);
1167
// calloc() expects the reported memory address range to be filled with 0's
1169
if (allocationType == m_alloc_calloc)
1171
memset(au->reportedAddress, 0, au->reportedSize);
1174
// Validate every single allocated unit in memory
1176
if (alwaysValidateAll) m_validateAllAllocUnits();
1180
if (alwaysLogAll) log("[+] ----> addr 0x%08X", (unsigned int) au->reportedAddress);
1182
// Resetting the globals insures that if at some later time, somebody calls our memory manager from an unknown
1183
// source (i.e. they didn't include our H file) then we won't think it was the last allocation.
1187
// Return the (reported) address of the new allocation unit
1189
#ifdef TEST_MEMORY_MANAGER
1190
log("[D] EXIT : m_allocator()");
1193
return au->reportedAddress;
1195
catch(const char *err)
1197
// Deal with the errors
1202
#ifdef TEST_MEMORY_MANAGER
1203
log("[D] EXIT : m_allocator()");
1210
// ---------------------------------------------------------------------------------------------------------------------------------
1211
// Reallocate memory and track it
1212
// ---------------------------------------------------------------------------------------------------------------------------------
1214
void *m_reallocator(const char *sourceFile, const unsigned int sourceLine, const char *sourceFunc, const unsigned int reallocationType, const size_t reportedSize, void *reportedAddress)
1218
#ifdef TEST_MEMORY_MANAGER
1219
log("[D] ENTER: m_reallocator()");
1222
// Calling realloc with a NULL should force same operations as a malloc
1224
if (!reportedAddress)
1226
return m_allocator(sourceFile, sourceLine, sourceFunc, reallocationType, reportedSize);
1229
// Increase our allocation count
1231
currentAllocationCount++;
1233
// If you hit this assert, you requested a breakpoint on a specific allocation count
1234
m_assert(currentAllocationCount != breakOnAllocationCount);
1238
if (alwaysLogAll) log("[~] %05d %8s of size 0x%08X(%08d) by %s", currentAllocationCount, allocationTypes[reallocationType], reportedSize, reportedSize, ownerString(sourceFile, sourceLine, sourceFunc));
1240
// Locate the existing allocation unit
1242
sAllocUnit *au = findAllocUnit(reportedAddress);
1244
// If you hit this assert, you tried to reallocate RAM that wasn't allocated by this memory manager.
1245
m_assert(au != NULL);
1246
if (au == NULL) throw "Request to reallocate RAM that was never allocated";
1248
// If you hit this assert, then the allocation unit that is about to be reallocated is damaged. But you probably
1249
// already know that from a previous assert you should have seen in validateAllocUnit() :)
1250
m_assert(m_validateAllocUnit(au));
1252
// If you hit this assert, then this reallocation was made from a source that isn't setup to use this memory
1253
// tracking software, use the stack frame to locate the source and include our H file.
1254
m_assert(reallocationType != m_alloc_unknown);
1256
// If you hit this assert, you were trying to reallocate RAM that was not allocated in a way that is compatible with
1257
// realloc. In other words, you have a allocation/reallocation mismatch.
1258
m_assert(au->allocationType == m_alloc_malloc ||
1259
au->allocationType == m_alloc_calloc ||
1260
au->allocationType == m_alloc_realloc);
1262
// If you hit this assert, then the "break on realloc" flag for this allocation unit is set (and will continue to be
1263
// set until you specifically shut it off. Interrogate the 'au' variable to determine information about this
1265
m_assert(au->breakOnRealloc == false);
1267
// Keep track of the original size
1269
unsigned int originalReportedSize = au->reportedSize;
1271
if (alwaysLogAll) log("[~] ----> from 0x%08X(%08d)", originalReportedSize, originalReportedSize);
1273
// Do the reallocation
1275
void *oldReportedAddress = reportedAddress;
1276
size_t newActualSize = calculateActualSize(reportedSize);
1277
void *newActualAddress = NULL;
1278
#ifdef RANDOM_FAILURE
1280
double b = RAND_MAX / 100.0 * RANDOM_FAILURE;
1283
newActualAddress = realloc(au->actualAddress, newActualSize);
1287
log("[F] Random faiure");
1290
newActualAddress = realloc(au->actualAddress, newActualSize);
1293
// We don't want to assert with random failures, because we want the application to deal with them.
1295
#ifndef RANDOM_FAILURE
1296
// If you hit this assert, then the requested allocation simply failed (you're out of memory) Interrogate the
1297
// variable 'au' to see the original allocation. You can also query 'newActualSize' to see the amount of memory
1298
// trying to be allocated. Finally, you can query 'reportedSize' to see how much memory was requested by the caller.
1299
m_assert(newActualAddress);
1302
if (!newActualAddress) throw "Request for reallocation failed. Out of memory.";
1304
// Remove this allocation from our stats (we'll add the new reallocation again later)
1306
stats.totalReportedMemory -= au->reportedSize;
1307
stats.totalActualMemory -= au->actualSize;
1309
// Update the allocation with the new information
1311
au->actualSize = newActualSize;
1312
au->actualAddress = newActualAddress;
1313
au->reportedSize = calculateReportedSize(newActualSize);
1314
au->reportedAddress = calculateReportedAddress(newActualAddress);
1315
au->allocationType = reallocationType;
1316
au->sourceLine = sourceLine;
1317
au->allocationNumber = currentAllocationCount;
1318
if (sourceFile) strncpy(au->sourceFile, sourceFileStripper(sourceFile), sizeof(au->sourceFile) - 1);
1319
else strcpy (au->sourceFile, "??");
1320
if (sourceFunc) strncpy(au->sourceFunc, sourceFunc, sizeof(au->sourceFunc) - 1);
1321
else strcpy (au->sourceFunc, "??");
1323
// The reallocation may cause the address to change, so we should relocate our allocation unit within the hash table
1325
unsigned int hashIndex = (unsigned int) -1;
1326
if (oldReportedAddress != au->reportedAddress)
1328
// Remove this allocation unit from the hash table
1331
unsigned int hashIndex = ((unsigned int) oldReportedAddress >> 4) & (hashSize - 1);
1332
if (hashTable[hashIndex] == au)
1334
hashTable[hashIndex] = hashTable[hashIndex]->next;
1338
if (au->prev) au->prev->next = au->next;
1339
if (au->next) au->next->prev = au->prev;
1343
// Re-insert it back into the hash table
1345
hashIndex = ((unsigned int) au->reportedAddress >> 4) & (hashSize - 1);
1346
if (hashTable[hashIndex]) hashTable[hashIndex]->prev = au;
1347
au->next = hashTable[hashIndex];
1349
hashTable[hashIndex] = au;
1352
// Account for the new allocatin unit in our stats
1354
stats.totalReportedMemory += au->reportedSize;
1355
stats.totalActualMemory += au->actualSize;
1356
if (stats.totalReportedMemory > stats.peakReportedMemory) stats.peakReportedMemory = stats.totalReportedMemory;
1357
if (stats.totalActualMemory > stats.peakActualMemory) stats.peakActualMemory = stats.totalActualMemory;
1358
int deltaReportedSize = reportedSize - originalReportedSize;
1359
if (deltaReportedSize > 0)
1361
stats.accumulatedReportedMemory += deltaReportedSize;
1362
stats.accumulatedActualMemory += deltaReportedSize;
1365
// Prepare the allocation unit for use (wipe it with recognizable garbage)
1367
wipeWithPattern(au, unusedPattern, originalReportedSize);
1369
// If you hit this assert, then something went wrong, because the allocation unit was properly validated PRIOR to
1370
// the reallocation. This should not happen.
1371
m_assert(m_validateAllocUnit(au));
1373
// Validate every single allocated unit in memory
1375
if (alwaysValidateAll) m_validateAllAllocUnits();
1379
if (alwaysLogAll) log("[~] ----> addr 0x%08X", (unsigned int) au->reportedAddress);
1381
// Resetting the globals insures that if at some later time, somebody calls our memory manager from an unknown
1382
// source (i.e. they didn't include our H file) then we won't think it was the last allocation.
1386
// Return the (reported) address of the new allocation unit
1388
#ifdef TEST_MEMORY_MANAGER
1389
log("[D] EXIT : m_reallocator()");
1392
return au->reportedAddress;
1394
catch(const char *err)
1396
// Deal with the errors
1401
#ifdef TEST_MEMORY_MANAGER
1402
log("[D] EXIT : m_reallocator()");
1409
// ---------------------------------------------------------------------------------------------------------------------------------
1410
// Deallocate memory and track it
1411
// ---------------------------------------------------------------------------------------------------------------------------------
1413
void m_deallocator(const char *sourceFile, const unsigned int sourceLine, const char *sourceFunc, const unsigned int deallocationType, const void *reportedAddress)
1417
#ifdef TEST_MEMORY_MANAGER
1418
log("[D] ENTER: m_deallocator()");
1423
if (alwaysLogAll) log("[-] ----- %8s of addr 0x%08X by %s", allocationTypes[deallocationType], (unsigned int) reportedAddress, ownerString(sourceFile, sourceLine, sourceFunc));
1425
// Go get the allocation unit
1427
sAllocUnit *au = findAllocUnit(reportedAddress);
1429
// If you hit this assert, you tried to deallocate RAM that wasn't allocated by this memory manager.
1430
m_assert(au != NULL);
1431
if (au == NULL) throw "Request to deallocate RAM that was never allocated";
1433
// If you hit this assert, then the allocation unit that is about to be deallocated is damaged. But you probably
1434
// already know that from a previous assert you should have seen in validateAllocUnit() :)
1435
m_assert(m_validateAllocUnit(au));
1437
// If you hit this assert, then this deallocation was made from a source that isn't setup to use this memory
1438
// tracking software, use the stack frame to locate the source and include our H file.
1439
m_assert(deallocationType != m_alloc_unknown);
1441
// If you hit this assert, you were trying to deallocate RAM that was not allocated in a way that is compatible with
1442
// the deallocation method requested. In other words, you have a allocation/deallocation mismatch.
1443
m_assert((deallocationType == m_alloc_delete && au->allocationType == m_alloc_new ) ||
1444
(deallocationType == m_alloc_delete_array && au->allocationType == m_alloc_new_array) ||
1445
(deallocationType == m_alloc_free && au->allocationType == m_alloc_malloc ) ||
1446
(deallocationType == m_alloc_free && au->allocationType == m_alloc_calloc ) ||
1447
(deallocationType == m_alloc_free && au->allocationType == m_alloc_realloc ) ||
1448
(deallocationType == m_alloc_unknown ) );
1450
// If you hit this assert, then the "break on dealloc" flag for this allocation unit is set. Interrogate the 'au'
1451
// variable to determine information about this allocation unit.
1452
m_assert(au->breakOnDealloc == false);
1454
// Wipe the deallocated RAM with a new pattern. This doen't actually do us much good in debug mode under WIN32,
1455
// because Microsoft's memory debugging & tracking utilities will wipe it right after we do. Oh well.
1457
wipeWithPattern(au, releasedPattern);
1459
// Do the deallocation
1461
free(au->actualAddress);
1463
// Remove this allocation unit from the hash table
1465
unsigned int hashIndex = ((unsigned int) au->reportedAddress >> 4) & (hashSize - 1);
1466
if (hashTable[hashIndex] == au)
1468
hashTable[hashIndex] = au->next;
1472
if (au->prev) au->prev->next = au->next;
1473
if (au->next) au->next->prev = au->prev;
1476
// Remove this allocation from our stats
1478
stats.totalReportedMemory -= au->reportedSize;
1479
stats.totalActualMemory -= au->actualSize;
1480
stats.totalAllocUnitCount--;
1482
// Add this allocation unit to the front of our reservoir of unused allocation units
1484
memset(au, 0, sizeof(sAllocUnit));
1485
au->next = reservoir;
1488
// Resetting the globals insures that if at some later time, somebody calls our memory manager from an unknown
1489
// source (i.e. they didn't include our H file) then we won't think it was the last allocation.
1493
// Validate every single allocated unit in memory
1495
if (alwaysValidateAll) m_validateAllAllocUnits();
1497
// If we're in the midst of static deinitialization time, track any pending memory leaks
1499
if (staticDeinitTime) dumpLeakReport();
1501
catch(const char *err)
1509
#ifdef TEST_MEMORY_MANAGER
1510
log("[D] EXIT : m_deallocator()");
1514
// ---------------------------------------------------------------------------------------------------------------------------------
1515
// -DOC- The following utilitarian allow you to become proactive in tracking your own memory, or help you narrow in on those tough
1517
// ---------------------------------------------------------------------------------------------------------------------------------
1519
bool m_validateAddress(const void *reportedAddress)
1521
// Just see if the address exists in our allocation routines
1523
return findAllocUnit(reportedAddress) != NULL;
1526
// ---------------------------------------------------------------------------------------------------------------------------------
1528
bool m_validateAllocUnit(const sAllocUnit *allocUnit)
1530
// Make sure the padding is untouched
1532
long *pre = (long *) allocUnit->actualAddress;
1533
long *post = (long *) ((char *)allocUnit->actualAddress + allocUnit->actualSize - paddingSize * sizeof(long));
1534
bool errorFlag = false;
1535
for (unsigned int i = 0; i < paddingSize; i++, pre++, post++)
1537
if (*pre != (long) prefixPattern)
1539
log("[!] A memory allocation unit was corrupt because of an underrun:");
1540
m_dumpAllocUnit(allocUnit, " ");
1544
// If you hit this assert, then you should know that this allocation unit has been damaged. Something (possibly the
1545
// owner?) has underrun the allocation unit (modified a few bytes prior to the start). You can interrogate the
1546
// variable 'allocUnit' to see statistics and information about this damaged allocation unit.
1547
m_assert(*pre == (long) prefixPattern);
1549
if (*post != (long) postfixPattern)
1551
log("[!] A memory allocation unit was corrupt because of an overrun:");
1552
m_dumpAllocUnit(allocUnit, " ");
1556
// If you hit this assert, then you should know that this allocation unit has been damaged. Something (possibly the
1557
// owner?) has overrun the allocation unit (modified a few bytes after the end). You can interrogate the variable
1558
// 'allocUnit' to see statistics and information about this damaged allocation unit.
1559
m_assert(*post == (long) postfixPattern);
1562
// Return the error status (we invert it, because a return of 'false' means error)
1567
// ---------------------------------------------------------------------------------------------------------------------------------
1569
bool m_validateAllAllocUnits()
1571
// Just go through each allocation unit in the hash table and count the ones that have errors
1573
unsigned int errors = 0;
1574
unsigned int allocCount = 0;
1575
for (unsigned int i = 0; i < hashSize; i++)
1577
sAllocUnit *ptr = hashTable[i];
1581
if (!m_validateAllocUnit(ptr)) errors++;
1586
// Test for hash-table correctness
1588
if (allocCount != stats.totalAllocUnitCount)
1590
log("[!] Memory tracking hash table corrupt!");
1594
// If you hit this assert, then the internal memory (hash table) used by this memory tracking software is damaged! The
1595
// best way to track this down is to use the alwaysLogAll flag in conjunction with STRESS_TEST macro to narrow in on the
1596
// offending code. After running the application with these settings (and hitting this assert again), interrogate the
1597
// memory.log file to find the previous successful operation. The corruption will have occurred between that point and this
1599
m_assert(allocCount == stats.totalAllocUnitCount);
1601
// If you hit this assert, then you've probably already been notified that there was a problem with a allocation unit in a
1602
// prior call to validateAllocUnit(), but this assert is here just to make sure you know about it. :)
1603
m_assert(errors == 0);
1607
if (errors) log("[!] While validting all allocation units, %d allocation unit(s) were found to have problems", errors);
1609
// Return the error status
1614
// ---------------------------------------------------------------------------------------------------------------------------------
1615
// -DOC- Unused RAM calculation routines. Use these to determine how much of your RAM is unused (in bytes)
1616
// ---------------------------------------------------------------------------------------------------------------------------------
1618
unsigned int m_calcUnused(const sAllocUnit *allocUnit)
1620
const unsigned long *ptr = (const unsigned long *) allocUnit->reportedAddress;
1621
unsigned int count = 0;
1623
for (unsigned int i = 0; i < allocUnit->reportedSize; i += sizeof(long), ptr++)
1625
if (*ptr == unusedPattern) count += sizeof(long);
1631
// ---------------------------------------------------------------------------------------------------------------------------------
1633
unsigned int m_calcAllUnused()
1635
// Just go through each allocation unit in the hash table and count the unused RAM
1637
unsigned int total = 0;
1638
for (unsigned int i = 0; i < hashSize; i++)
1640
sAllocUnit *ptr = hashTable[i];
1643
total += m_calcUnused(ptr);
1651
// ---------------------------------------------------------------------------------------------------------------------------------
1652
// -DOC- The following functions are for logging and statistics reporting.
1653
// ---------------------------------------------------------------------------------------------------------------------------------
1655
void m_dumpAllocUnit(const sAllocUnit *allocUnit, const char *prefix)
1657
log("[I] %sAddress (reported): %010p", prefix, allocUnit->reportedAddress);
1658
log("[I] %sAddress (actual) : %010p", prefix, allocUnit->actualAddress);
1659
log("[I] %sSize (reported) : 0x%08X (%s)", prefix, allocUnit->reportedSize, memorySizeString(allocUnit->reportedSize));
1660
log("[I] %sSize (actual) : 0x%08X (%s)", prefix, allocUnit->actualSize, memorySizeString(allocUnit->actualSize));
1661
log("[I] %sOwner : %s(%d)::%s", prefix, allocUnit->sourceFile, allocUnit->sourceLine, allocUnit->sourceFunc);
1662
log("[I] %sAllocation type : %s", prefix, allocationTypes[allocUnit->allocationType]);
1663
log("[I] %sAllocation number : %d", prefix, allocUnit->allocationNumber);
1666
// ---------------------------------------------------------------------------------------------------------------------------------
1668
void m_dumpMemoryReport(const char *filename, const bool overwrite)
1670
// Open the report file
1674
if (overwrite) fp = fopen(filename, "w+b");
1675
else fp = fopen(filename, "ab");
1677
// If you hit this assert, then the memory report generator is unable to log information to a file (can't open the file for
1684
static char timeString[25];
1685
memset(timeString, 0, sizeof(timeString));
1686
time_t t = time(NULL);
1687
struct tm *tme = localtime(&t);
1688
fprintf(fp, " ---------------------------------------------------------------------------------------------------------------------------------- \r\n");
1689
fprintf(fp, "| Memory report for: %02d/%02d/%04d %02d:%02d:%02d |\r\n", tme->tm_mon + 1, tme->tm_mday, tme->tm_year + 1900, tme->tm_hour, tme->tm_min, tme->tm_sec);
1690
fprintf(fp, " ---------------------------------------------------------------------------------------------------------------------------------- \r\n");
1691
fprintf(fp, "\r\n");
1692
fprintf(fp, "\r\n");
1696
fprintf(fp, " ---------------------------------------------------------------------------------------------------------------------------------- \r\n");
1697
fprintf(fp, "| T O T A L S |\r\n");
1698
fprintf(fp, " ---------------------------------------------------------------------------------------------------------------------------------- \r\n");
1699
fprintf(fp, " Allocation unit count: %10s\r\n", insertCommas(stats.totalAllocUnitCount));
1700
fprintf(fp, " Reported to application: %s\r\n", memorySizeString(stats.totalReportedMemory));
1701
fprintf(fp, " Actual total memory in use: %s\r\n", memorySizeString(stats.totalActualMemory));
1702
fprintf(fp, " Memory tracking overhead: %s\r\n", memorySizeString(stats.totalActualMemory - stats.totalReportedMemory));
1703
fprintf(fp, "\r\n");
1705
fprintf(fp, " ---------------------------------------------------------------------------------------------------------------------------------- \r\n");
1706
fprintf(fp, "| P E A K S |\r\n");
1707
fprintf(fp, " ---------------------------------------------------------------------------------------------------------------------------------- \r\n");
1708
fprintf(fp, " Allocation unit count: %10s\r\n", insertCommas(stats.peakAllocUnitCount));
1709
fprintf(fp, " Reported to application: %s\r\n", memorySizeString(stats.peakReportedMemory));
1710
fprintf(fp, " Actual: %s\r\n", memorySizeString(stats.peakActualMemory));
1711
fprintf(fp, " Memory tracking overhead: %s\r\n", memorySizeString(stats.peakActualMemory - stats.peakReportedMemory));
1712
fprintf(fp, "\r\n");
1714
fprintf(fp, " ---------------------------------------------------------------------------------------------------------------------------------- \r\n");
1715
fprintf(fp, "| A C C U M U L A T E D |\r\n");
1716
fprintf(fp, " ---------------------------------------------------------------------------------------------------------------------------------- \r\n");
1717
fprintf(fp, " Allocation unit count: %s\r\n", memorySizeString(stats.accumulatedAllocUnitCount));
1718
fprintf(fp, " Reported to application: %s\r\n", memorySizeString(stats.accumulatedReportedMemory));
1719
fprintf(fp, " Actual: %s\r\n", memorySizeString(stats.accumulatedActualMemory));
1720
fprintf(fp, "\r\n");
1722
fprintf(fp, " ---------------------------------------------------------------------------------------------------------------------------------- \r\n");
1723
fprintf(fp, "| U N U S E D |\r\n");
1724
fprintf(fp, " ---------------------------------------------------------------------------------------------------------------------------------- \r\n");
1725
fprintf(fp, " Memory allocated but not in use: %s\r\n", memorySizeString(m_calcAllUnused()));
1726
fprintf(fp, "\r\n");
1728
dumpAllocations(fp);
1733
// ---------------------------------------------------------------------------------------------------------------------------------
1735
sMStats m_getMemoryStatistics()
1740
// ---------------------------------------------------------------------------------------------------------------------------------
1741
// mmgr.cpp - End of file
1742
// ---------------------------------------------------------------------------------------------------------------------------------