00001 /*------------------------------------------------------------------------- 00002 This source file is a part of OGRE 00003 (Object-oriented Graphics Rendering Engine) 00004 00005 For the latest info, see http://www.ogre3d.org/ 00006 00007 Copyright © 2000-2002 The OGRE Team 00008 Also see acknowledgements in Readme.html 00009 00010 This library is free software; you can redistribute it and/or modify it 00011 under the terms of the GNU Lesser General Public License (LGPL) as 00012 published by the Free Software Foundation; either version 2.1 of the 00013 License, or (at your option) any later version. 00014 00015 This library is distributed in the hope that it will be useful, but 00016 WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY 00017 or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public 00018 License for more details. 00019 00020 You should have received a copy of the GNU Lesser General Public License 00021 along with this library; if not, write to the Free Software Foundation, 00022 Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA or go to 00023 http://www.gnu.org/copyleft/lesser.txt 00024 -------------------------------------------------------------------------*/ 00025 #include "OgreStableHeaders.h" 00026 //---- ORIGINAL COPYRIGHT FOLLOWS ------------------------------------------- 00027 // --------------------------------------------------------------------------------------------------------------------------------- 00028 // Copyright 2000, Paul Nettle. All rights reserved. 00029 // 00030 // You are free to use this source code in any commercial or non-commercial product. 00031 // 00032 // mmgr.cpp - Memory manager & tracking software 00033 // 00034 // The most recent version of this software can be found at: ftp://ftp.GraphicsPapers.com/pub/ProgrammingTools/MemoryManagers/ 00035 // 00036 // [NOTE: Best when viewed with 8-character tabs] 00037 // 00038 // --------------------------------------------------------------------------------------------------------------------------------- 00039 00040 #include "OgreMemoryManager.h" 00041 00042 //----------------------------------------------------------------------------- 00043 // Allow the use of the real *alloc/free/new/delete functions 00044 #include "OgreNoMemoryMacros.h" 00045 //----------------------------------------------------------------------------- 00046 00047 namespace Ogre 00048 { 00049 00050 //----------------------------------------------------------------------------- 00051 MemoryManager MemoryManager::sMemManager; 00052 //----------------------------------------------------------------------------- 00053 00054 #if OGRE_DEBUG_MEMORY_MANAGER && OGRE_DEBUG_MODE 00055 00056 #define OGRE_MEMMANAGER_STRESS_TEST 0 00057 00058 #if OGRE_MEMORY_STRESS_TEST 00059 00060 bool randomWipe = true; 00061 bool alwaysValidateAll = true; 00062 bool alwaysLogAll = true; 00063 bool alwaysWipeAll = false; 00064 bool cleanupLogOnFirstRun = true; 00065 00066 const unsigned int hashBits = 24; 00067 const unsigned int paddingSize = 1024; // An extra 8K per allocation! 00068 00069 #else 00070 00071 bool randomWipe = false; 00072 bool alwaysValidateAll = false; 00073 bool alwaysLogAll = false; 00074 bool alwaysWipeAll = true; 00075 bool cleanupLogOnFirstRun = true; 00076 00077 const unsigned int hashBits = 24; 00078 const unsigned int paddingSize = 4; 00079 00080 #endif 00081 00082 //--------------------------------------------------------------------------------------------- 00083 // We define our own assert, because we don't want to bring up an assertion dialog, since that 00084 // allocates RAM. Our new assert simply declares a forced breakpoint. 00085 // 00086 // The BEOS assert added by Arvid Norberg <arvid@iname.com>. 00087 #ifdef WIN32 00088 #ifdef _DEBUG 00089 #define m_assert(x) { if( (x) == false ) __asm { int 3 } } 00090 #else 00091 #define m_assert(x) 00092 #endif 00093 #elif defined(__BEOS__) 00094 #ifdef DEBUG 00095 extern void debugger(const char *message); 00096 #define m_assert(x) { if( (x) == false ) debugger("mmgr: assert failed") } 00097 #else 00098 #define m_assert(x) 00099 #endif 00100 #else // We can use this safely on *NIX, since it doesn't bring up a dialog window. 00101 #define m_assert(cond) assert(cond) 00102 #endif 00103 //--------------------------------------------------------------------------------------------- 00104 00105 //--------------------------------------------------------------------------------------------- 00106 // Here, we turn off our macros because any place in this source file where the word 'new' or 00107 // the word 'delete' (etc.) appear will be expanded by the macro. So to avoid problems using 00108 // them within this source file, we'll just #undef them. 00109 #include "OgreNoMemoryMacros.h" 00110 //--------------------------------------------------------------------------------------------- 00111 00112 //--------------------------------------------------------------------------------------------- 00113 // Get to know these values. They represent the values that will be used to fill unused and 00114 // deallocated RAM. 00115 unsigned int prefixPattern = 0xbaadf00d; // Fill pattern for bytes preceeding allocated blocks 00116 unsigned int postfixPattern = 0xdeadc0de; // Fill pattern for bytes following allocated blocks 00117 unsigned int unusedPattern = 0xfeedface; // Fill pattern for freshly allocated blocks 00118 unsigned int releasedPattern = 0xdeadbeef; // Fill pattern for deallocated blocks 00119 //--------------------------------------------------------------------------------------------- 00120 00121 //--------------------------------------------------------------------------------------------- 00122 // Other locals 00123 const unsigned int hashSize = 1 << hashBits; 00124 const char *allocationTypes[] = 00125 { 00126 "Unknown", 00127 "new", 00128 "new[]", 00129 "malloc", 00130 "calloc", 00131 "realloc", 00132 "delete", 00133 "delete[]", 00134 "free" 00135 }; 00136 //--------------------------------------------------------------------------------------------- 00137 00138 sAllocUnit *hashTable[hashSize]; 00139 sAllocUnit *reservoir = NULL; 00140 00141 unsigned int currentAllocationCount = 0; 00142 unsigned int breakOnAllocationCount = 0; 00143 00144 sMStats stats; 00145 00146 const char *sourceFile = "??"; 00147 const char *sourceFunc = "??"; 00148 unsigned int sourceLine = 0; 00149 00150 sAllocUnit **reservoirBuffer = NULL; 00151 unsigned int reservoirBufferSize = 0; 00152 00153 const char *memoryLogFile = "OgreMemory.log"; 00154 const char *memoryLeakLogFile = "OgreLeaks.log"; 00155 00156 void doCleanupLogOnFirstRun(); 00157 00158 //--------------------------------------------------------------------------------------------- 00159 // Local functions only 00160 //--------------------------------------------------------------------------------------------- 00161 00162 //--------------------------------------------------------------------------------------------- 00165 void log( const char *format, ... ) 00166 { 00167 // The buffer 00168 char buffer[2048]; 00169 00170 va_list ap; 00171 va_start( ap, format ); 00172 vsprintf( buffer, format, ap ); 00173 va_end( ap ); 00174 00175 // Cleanup the log? 00176 00177 if( cleanupLogOnFirstRun ) 00178 doCleanupLogOnFirstRun(); 00179 00180 // Open the log file 00181 FILE *fp = fopen( memoryLogFile, "ab" ); 00182 00183 // If you hit this assert, then the memory logger is unable to log 00184 // information to a file (can't open the file for some reason.) You can 00185 // interrogate the variable 'buffer' to see what was supposed to be logged 00186 // (but won't be.) 00187 m_assert(fp); 00188 00189 if( !fp ) 00190 return; 00191 00192 // Spit out the data to the log 00193 fprintf( fp, "%s\r\n", buffer ); 00194 00195 fclose( fp ); 00196 } 00197 00198 //--------------------------------------------------------------------------------------------- 00201 void doCleanupLogOnFirstRun() 00202 { 00203 if( cleanupLogOnFirstRun ) 00204 { 00205 unlink( memoryLogFile ); 00206 cleanupLogOnFirstRun = false; 00207 00208 // Print a header for the log 00209 time_t t = time(NULL); 00210 log("--------------------------------------------------------------------------------"); 00211 log(""); 00212 log(" %s - Memory logging file created on %s", memoryLogFile, asctime(localtime(&t))); 00213 log("--------------------------------------------------------------------------------"); 00214 log(""); 00215 log("This file contains a log of all memory operations performed during the last run."); 00216 log(""); 00217 log("Interrogate this file to track errors or to help track down memory-related"); 00218 log("issues. You can do this by tracing the allocations performed by a specific owner"); 00219 log("or by tracking a specific address through a series of allocations and"); 00220 log("reallocations."); 00221 log(""); 00222 log("There is a lot of useful information here which, when used creatively, can be"); 00223 log("extremely helpful."); 00224 log(""); 00225 log("Note that the following guides are used throughout this file:"); 00226 log(""); 00227 log(" [!] - Error"); 00228 log(" [+] - Allocation"); 00229 log(" [~] - Reallocation"); 00230 log(" [-] - Deallocation"); 00231 log(" [I] - Generic information"); 00232 log(" [F] - Failure induced for the purpose of stress-testing your application"); 00233 log(" [D] - Information used for debugging this memory manager"); 00234 log(""); 00235 log("...so, to find all errors in the file, search for \"[!]\""); 00236 log(""); 00237 log("--------------------------------------------------------------------------------"); 00238 } 00239 } 00240 00241 //--------------------------------------------------------------------------------------------- 00248 const char *sourceFileStripper( const char *sourceFile ) 00249 { 00250 char *ptr = strrchr(sourceFile, '\\'); 00251 if( ptr ) 00252 return ptr + 1; 00253 ptr = strrchr( sourceFile, '/' ); 00254 if( ptr ) 00255 return ptr + 1; 00256 return sourceFile; 00257 } 00258 00259 //--------------------------------------------------------------------------------------------- 00264 const char *ownerString( 00265 const char *sourceFile, 00266 const unsigned int sourceLine, 00267 const char *sourceFunc ) 00268 { 00269 static char str[90]; 00270 snprintf( str, 89, "%s(%05d)::%s", 00271 sourceFileStripper(sourceFile), 00272 sourceLine, 00273 sourceFunc); 00274 return str; 00275 } 00276 00277 //--------------------------------------------------------------------------------------------- 00283 const char *insertCommas( size_t value ) 00284 { 00285 static char str[30]; 00286 00287 // This pointer is used to add digits moving backwards in the string. 00288 char *p = &str[28]; 00289 // The current digit 00290 int c_digit = 1; 00291 00292 // Set the last character in the string to NULL. 00293 str[29] = 0; 00294 00295 // While we've still got some digits in value, add them. 00296 while( value ) 00297 { 00298 *p++ = '0' + (char)( value % 10 ); value /= 10; 00299 00300 // If the digit which was inserted was at the end of a group, add a comma. 00301 if( !( c_digit % 3 ) ) 00302 *p++ = ','; 00303 00304 c_digit++; 00305 } 00306 00307 // Now return the offset in the static string above. 00308 return ++p; 00309 } 00310 00311 //--------------------------------------------------------------------------------------------- 00316 const char *memorySizeString( size_t size ) 00317 { 00318 static char str[90]; 00319 00320 if( size > 1048576 ) 00321 sprintf( str, "%10s (%7.2fM)", insertCommas( size ), (float) size / 1048576.0f ); 00322 else if( size > 1024 ) 00323 sprintf( str, "%10s (%7.2fK)", insertCommas( size ), (float) size / 1024.0f ); 00324 else 00325 sprintf( str, "%10s bytes ", insertCommas( size ) ); 00326 return str; 00327 } 00328 00329 //--------------------------------------------------------------------------------------------- 00332 sAllocUnit *findAllocUnit(const void *reportedAddress) 00333 { 00334 // Just in case... 00335 m_assert( reportedAddress != NULL ); 00336 00337 // Use the address to locate the hash index. Note that we shift off the 00338 // lower four bits. This is because most allocated addresses will be on 00339 // four-, eight- or even sixteen-byte boundaries. If we didn't do this, 00340 // the hash index would not have very good coverage. 00341 00342 size_t hashIndex = ( (size_t)reportedAddress >> 4 ) & ( hashSize - 1 ); 00343 sAllocUnit *ptr = hashTable[ hashIndex ]; 00344 while( ptr ) 00345 { 00346 if( ptr->reportedAddress == reportedAddress ) 00347 return ptr; 00348 ptr = ptr->next; 00349 } 00350 00351 return NULL; 00352 } 00353 00354 // --------------------------------------------------------------------------------------------------------------------------------- 00355 inline static size_t calculateActualSize( const size_t reportedSize ) 00356 { 00357 // We use DWORDS as our padding, and a long is guaranteed to be 4 bytes, 00358 // but an int is not (ANSI defines an int as being the standard word size 00359 // for a processor; on a 32-bit machine, that's 4 bytes, but on a 64-bit 00360 // machine, it's 8 bytes, which means an int can actually be larger than 00361 // a long.) 00362 00363 return reportedSize + paddingSize * sizeof(long) * 2; 00364 } 00365 00366 // --------------------------------------------------------------------------------------------------------------------------------- 00367 inline static size_t calculateReportedSize( const size_t actualSize ) 00368 { 00369 // We use DWORDS as our padding, and a long is guaranteed to be 4 bytes, 00370 // but an int is not (ANSI defines an int as being the standard word size 00371 // for a processor; on a 32-bit machine, that's 4 bytes, but on a 64-bit 00372 // machine, it's 8 bytes, which means an int can actually be larger than a 00373 // long.) 00374 00375 return actualSize - paddingSize * sizeof(long) * 2; 00376 } 00377 00378 // --------------------------------------------------------------------------------------------------------------------------------- 00379 inline void *calculateReportedAddress( const void *actualAddress ) 00380 { 00381 // We allow this... 00382 if (!actualAddress) 00383 return NULL; 00384 00385 // Just account for the padding 00386 return (void *)((char *) actualAddress + sizeof(long) * paddingSize ); 00387 } 00388 00389 // --------------------------------------------------------------------------------------------------------------------------------- 00390 void wipeWithPattern( 00391 sAllocUnit *allocUnit, 00392 unsigned long pattern, 00393 const size_t originalReportedSize = 0 ) 00394 { 00395 // For a serious test run, we use wipes of random a random value. However, 00396 // if this causes a crash, we don't want it to crash in a different place 00397 // each time, so we specifically DO NOT call srand. If, by chance your 00398 // program calls srand(), you may wish to disable that when running with a 00399 // random wipe test. This will make any crashes more consistent so they 00400 // can be tracked down easier. 00401 if( randomWipe ) 00402 { 00403 pattern = ((rand() & 0xff) << 24) | ((rand() & 0xff) << 16) | ((rand() & 0xff) << 8) | (rand() & 0xff); 00404 } 00405 00406 // -DOC- We should wipe with 0's if we're not in debug mode, so we can help 00407 // hide bugs if possible when we release the product. So uncomment the 00408 // following line for releases. 00409 // 00410 // Note that the "alwaysWipeAll" should be turned on for this to have 00411 // effect, otherwise it won't do much good. But we will leave it this way (as 00412 // an option) because this does slow things down. 00413 00414 // pattern = 0; 00415 00416 // This part of the operation is optional 00417 if( alwaysWipeAll && allocUnit->reportedSize > originalReportedSize ) 00418 { 00419 // Fill the bulk 00420 long *lptr = (long *) ((char *)allocUnit->reportedAddress + originalReportedSize); 00421 size_t length = allocUnit->reportedSize - originalReportedSize; 00422 size_t i; 00423 for( i = 0; i < (length >> 2); i++, lptr++ ) 00424 { 00425 *lptr = pattern; 00426 } 00427 00428 // Fill the remainder 00429 unsigned int shiftCount = 0; 00430 char *cptr = (char *) lptr; 00431 for( i = 0; i < ( length & 0x3 ); i++, cptr++, shiftCount += 8 ) 00432 { 00433 *cptr = (char)((( pattern & ( 0xff << shiftCount ) ) >> shiftCount) & 0xff); 00434 } 00435 } 00436 00437 // Write in the prefix/postfix bytes 00438 long *pre = (long *)allocUnit->actualAddress; 00439 long *post = (long *)((char *)allocUnit->actualAddress + allocUnit->actualSize - paddingSize * sizeof(long)); 00440 for (unsigned int i = 0; i < paddingSize; i++, pre++, post++) 00441 { 00442 *pre = prefixPattern; 00443 *post = postfixPattern; 00444 } 00445 } 00446 00447 // --------------------------------------------------------------------------------------------------------------------------------- 00448 void dumpAllocations(FILE *fp) 00449 { 00450 fprintf(fp, "Alloc. Addr Size Addr Size BreakOn BreakOn \r\n"); 00451 fprintf(fp, "Number Reported Reported Actual Actual Unused Method Dealloc Realloc Allocated by \r\n"); 00452 fprintf(fp, "------ ---------- ---------- ---------- ---------- ---------- -------- ------- ------- --------------------------------------------------- \r\n"); 00453 00454 for( unsigned int i = 0; i < hashSize; i++ ) 00455 { 00456 sAllocUnit *ptr = hashTable[i]; 00457 while( ptr ) 00458 { 00459 fprintf(fp, "%06d 0x%08X 0x%08X 0x%08X 0x%08X 0x%08X %-8s %c %c %s(%d) %s\r\n", 00460 ptr->allocationNumber, 00461 (size_t) ptr->reportedAddress, ptr->reportedSize, 00462 (size_t) ptr->actualAddress, ptr->actualSize, 00463 MemoryManager::sMemManager.calcUnused(ptr), 00464 allocationTypes[ptr->allocationType], 00465 ptr->breakOnDealloc ? 'Y':'N', 00466 ptr->breakOnRealloc ? 'Y':'N', 00467 ptr->sourceFile, ptr->sourceLine, ptr->sourceFunc 00468 /*ownerString(ptr->sourceFile, ptr->sourceLine, ptr->sourceFunc)*/); 00469 ptr = ptr->next; 00470 } 00471 } 00472 } 00473 00474 // --------------------------------------------------------------------------------------------------------------------------------- 00475 void dumpLeakReport() 00476 { 00477 // Open the report file 00478 FILE *fp = fopen(memoryLeakLogFile, "w+b"); 00479 00480 // If you hit this assert, then the memory report generator is unable to 00481 // log information to a file (can't open the file for some reason.) 00482 m_assert(fp); 00483 00484 if( !fp ) 00485 return; 00486 00487 // Any leaks? 00488 00489 // Header 00490 char timeString[25]; 00491 memset( timeString, 0, sizeof(timeString) ); 00492 time_t t = time(NULL); 00493 struct tm *tme = localtime(&t); 00494 00495 fprintf(fp, " ---------------------------------------------------------------------------------------------------------------------------------- \r\n"); 00496 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); 00497 fprintf(fp, " ---------------------------------------------------------------------------------------------------------------------------------- \r\n"); 00498 fprintf(fp, "\r\n"); 00499 fprintf(fp, "\r\n"); 00500 00501 if( stats.totalAllocUnitCount ) 00502 { 00503 fprintf(fp, "%d memory leak%s found:\r\n", 00504 stats.totalAllocUnitCount, 00505 stats.totalAllocUnitCount == 1 ? "":"s" ); 00506 } 00507 else 00508 { 00509 fprintf(fp, "Congratulations! No memory leaks found!\r\n"); 00510 00511 // We can finally free up our own memory allocations 00512 if (reservoirBuffer) 00513 { 00514 for (unsigned int i = 0; i < reservoirBufferSize; i++) 00515 { 00516 free( reservoirBuffer[i] ); 00517 } 00518 free( reservoirBuffer ); 00519 reservoirBuffer = NULL; 00520 reservoirBufferSize = 0; 00521 reservoir = NULL; 00522 } 00523 } 00524 fprintf(fp, "\r\n"); 00525 00526 if( stats.totalAllocUnitCount ) 00527 { 00528 dumpAllocations(fp); 00529 } 00530 00531 fclose(fp); 00532 } 00533 00537 bool& MemoryManager::breakOnDealloc(void *reportedAddress) 00538 { 00539 #ifdef _DEBUG 00540 // Locate the existing allocation unit 00541 sAllocUnit *au = findAllocUnit( reportedAddress ); 00542 00543 // If you hit this assert, you tried to set a breakpoint on deallocation 00544 // for an address that doesn't exist. Interrogate the stack frame or the 00545 // variable 'au' to see which allocation this is. 00546 m_assert(au != NULL); 00547 00548 return au->breakOnDealloc; 00549 #else 00550 static bool b; 00551 return b; 00552 #endif 00553 } 00554 00559 void MemoryManager::breakOnAlloc(unsigned int count) 00560 { 00561 #ifdef _DEBUG 00562 breakOnAllocationCount = count; 00563 #endif 00564 } 00565 00566 void MemoryManager::setOwner(const char *file, const unsigned int line, const char *func) 00567 { 00568 // You're probably wondering about this... 00569 // 00570 // It's important for this memory manager to primarily work with global 00571 // new/delete in their original forms (i.e. with no extra parameters.) In 00572 // order to do this, we use macros that call this function prior to 00573 // operators new & delete. This is fine... usually. Here's what actually 00574 // happens when you use this macro to delete an object: 00575 // 00576 // setOwner( __FILE__, __LINE__, __FUNCTION__ ) --> object::~object() --> delete 00577 // 00578 // Note that the compiler inserts a call to the object's destructor just 00579 // prior to calling our overridden operator delete. 00580 // 00581 // But what happens when we delete an object whose destructor deletes 00582 // another object, whose desctuctor deletes another object? Here's a 00583 // diagram (indentation follows stack depth): 00584 // 00585 // setOwner(...) -> ~obj1() // original call to delete obj1 00586 // setOwner(...) -> ~obj2() // obj1's destructor deletes obj2 00587 // setOwner(...) -> ~obj3() // obj2's destructor deletes obj3 00588 // ... // obj3's destructor just does some stuff 00589 // delete // back in obj2's destructor, we call delete 00590 // delete // back in obj1's destructor, we call delete 00591 // delete // back to our original call, we call delete 00592 // 00593 // Because setOwner() just sets up some variables (below) it's important 00594 // that each call to setOwner() and successive calls to new/delete 00595 // alternate. However, in this case, three calls to setOwner() happen in 00596 // succession followed by three calls to delete in succession (with a few 00597 // calls to destructors mixed in for fun.) This means that only the final 00598 // call to delete (in this chain of events) will have the proper reporting, 00599 // and the first two in the chain will not have ANY owner-reporting 00600 // information. The deletes will still work fine, we just won't know who 00601 // called us. 00602 // 00603 // "Then build a stack, my friend!" you might think... but it's a very 00604 // common thing that people will be working with third-party libraries 00605 // (including MFC under Windows) which is not compiled with this memory 00606 // manager's macros. In those cases, setOwner() is never called, and 00607 // rightfully should not have the proper trace-back information. So if one 00608 // of the destructors in the chain ends up being a call to a delete from 00609 // a non-mmgr-compiled library, the stack will get confused. 00610 // 00611 // I've been unable to find a solution to this problem, but at least we can 00612 // detect it and report the data before we lose it. That's what this is all 00613 // about. It makes it somewhat confusing to read in the logs, but at least 00614 // ALL the information is present... 00615 // 00616 // There's a caveat here... The compiler is not required to call operator 00617 // delete if the value being deleted is NULL. In this case, any call to 00618 // delete with a NULL will sill call setOwner(), which will make 00619 // setOwner() think that there is a destructor chain becuase we setup the 00620 // variables, but nothing gets called to clear them. Because of this we 00621 // report a "Possible destructor chain". 00622 // 00623 // Thanks to J. Woznack (from Kodiak Interactive Software Studios -- 00624 // www.kodiakgames.com) for pointing this out. 00625 00626 if( sourceLine && alwaysLogAll ) 00627 { 00628 log( "[I] NOTE! Possible destructor chain: previous owner is %s", 00629 ownerString(sourceFile, sourceLine, sourceFunc) ); 00630 } 00631 00632 // Okay... save this stuff off so we can keep track of the caller 00633 sourceFile = file; 00634 sourceLine = line; 00635 sourceFunc = func; 00636 } 00637 00638 void resetGlobals() 00639 { 00640 sourceFile = "??"; 00641 sourceLine = 0; 00642 sourceFunc = "??"; 00643 } 00644 00647 void* MemoryManager::allocMem( 00648 const char *sourceFile, 00649 const unsigned int sourceLine, 00650 const char *sourceFunc, 00651 const unsigned int allocationType, 00652 const size_t reportedSize, 00653 const unsigned processID ) 00654 { 00655 // If we don't have a process ID yet, get one now 00656 if( !gProcessID ) 00657 gProcessID = sMemManager._getProcessID(); 00658 00659 try 00660 { 00661 // Increase our allocation count 00662 currentAllocationCount++; 00663 00664 // Log the request 00665 if( alwaysLogAll ) 00666 log("[+] %05d %8s of size 0x%08X(%08d) by %s", 00667 currentAllocationCount, 00668 allocationTypes[allocationType], 00669 reportedSize, 00670 reportedSize, 00671 ownerString(sourceFile, sourceLine, sourceFunc) ); 00672 00673 // If you hit this assert, you requested a breakpoint on a specific 00674 // allocation count 00675 m_assert( currentAllocationCount != breakOnAllocationCount ); 00676 00677 // If necessary, grow the reservoir of unused allocation units 00678 if( !reservoir ) 00679 { 00680 // Allocate 256 reservoir elements 00681 reservoir = (sAllocUnit *) malloc( sizeof(sAllocUnit) * 256 ); 00682 00683 // If you hit this assert, then the memory manager failed to 00684 // allocate internal memory for tracking the allocations 00685 m_assert( reservoir != NULL ); 00686 00687 // Danger Will Robinson! 00688 if( reservoir == NULL ) 00689 throw "Unable to allocate RAM for internal memory tracking data"; 00690 00691 // Build a linked-list of the elements in our reservoir 00692 memset( reservoir, 0, sizeof(sAllocUnit) * 256 ); 00693 for (unsigned int i = 0; i < 256 - 1; i++) 00694 { 00695 reservoir[i].next = &reservoir[i+1]; 00696 } 00697 00698 // Add this address to our reservoirBuffer so we can free it later 00699 sAllocUnit **temp = (sAllocUnit **)realloc( reservoirBuffer, (reservoirBufferSize + 1) * sizeof(sAllocUnit *) ); 00700 m_assert( temp ); 00701 if( temp ) 00702 { 00703 reservoirBuffer = temp; 00704 reservoirBuffer[reservoirBufferSize++] = reservoir; 00705 } 00706 } 00707 00708 // Logical flow says this should never happen... 00709 m_assert( reservoir != NULL ); 00710 00711 // Grab a new allocaton unit from the front of the reservoir 00712 sAllocUnit *au = reservoir; 00713 reservoir = au->next; 00714 00715 // Populate it with some real data 00716 // HACK the allocator should not need to memset the sAllocUnit 00717 // memset( au, 0, sizeof(sAllocUnit) ); 00718 au->actualSize = calculateActualSize(reportedSize); 00719 #ifdef RANDOM_FAILURE 00720 double a = rand(); 00721 double b = RAND_MAX / 100.0 * RANDOM_FAILURE; 00722 if( a > b ) 00723 { 00724 au->actualAddress = malloc( au->actualSize ); 00725 } 00726 else 00727 { 00728 log("[F] Random faiure"); 00729 au->actualAddress = NULL; 00730 } 00731 #else 00732 au->actualAddress = malloc(au->actualSize); 00733 #endif 00734 au->reportedSize = reportedSize; 00735 au->reportedAddress = calculateReportedAddress( au->actualAddress ); 00736 au->allocationType = allocationType; 00737 au->sourceLine = sourceLine; 00738 au->allocationNumber = currentAllocationCount; 00739 au->processID = processID; 00740 00741 if( sourceFile ) 00742 strncpy( au->sourceFile, sourceFileStripper(sourceFile), sizeof(au->sourceFile) - 1 ); 00743 else 00744 strcpy( au->sourceFile, "??" ); 00745 00746 if( sourceFunc ) 00747 strncpy( au->sourceFunc, sourceFunc, sizeof(au->sourceFunc) - 1 ); 00748 else 00749 strcpy( au->sourceFunc, "??" ); 00750 00751 // We don't want to assert with random failures, because we want the application to deal with them. 00752 00753 #ifndef RANDOM_FAILURE 00754 // If you hit this assert, then the requested allocation simply failed 00755 // (you're out of memory.) Interrogate the variable 'au' or the stack 00756 // frame to see what you were trying to do. 00757 m_assert( au->actualAddress != NULL ); 00758 #endif 00759 00760 if( au->actualAddress == NULL ) 00761 { 00762 throw "Request for allocation failed. Out of memory."; 00763 } 00764 00765 // If you hit this assert, then this allocation was made from a source 00766 // that isn't setup to use this memory tracking software, use the stack 00767 // frame to locate the source and include our H file. 00768 m_assert( allocationType != m_alloc_unknown ); 00769 00770 if( allocationType == m_alloc_unknown ) 00771 { 00772 log( "[!] Allocation made from outside memory tracker in %s(%s)::%s:", au->sourceFile, au->sourceLine, au->sourceFunc ); 00773 dumpAllocUnit( au, " " ); 00774 } 00775 00776 // Insert the new allocation into the hash table 00777 size_t hashIndex = ((size_t) au->reportedAddress >> 4) & (hashSize - 1); 00778 if( hashTable[hashIndex]) 00779 { 00780 hashTable[hashIndex]->prev = au; 00781 } 00782 au->next = hashTable[hashIndex]; 00783 au->prev = NULL; 00784 hashTable[hashIndex] = au; 00785 00786 // Account for the new allocatin unit in our stats 00787 stats.totalReportedMemory += au->reportedSize; 00788 stats.totalActualMemory += au->actualSize; 00789 stats.totalAllocUnitCount++; 00790 00791 if( stats.totalReportedMemory > stats.peakReportedMemory ) 00792 stats.peakReportedMemory = stats.totalReportedMemory; 00793 if( stats.totalActualMemory > stats.peakActualMemory ) 00794 stats.peakActualMemory = stats.totalActualMemory; 00795 if( stats.totalAllocUnitCount > stats.peakAllocUnitCount ) 00796 stats.peakAllocUnitCount = stats.totalAllocUnitCount; 00797 00798 stats.accumulatedReportedMemory += au->reportedSize; 00799 stats.accumulatedActualMemory += au->actualSize; 00800 stats.accumulatedAllocUnitCount++; 00801 00802 // Prepare the allocation unit for use (wipe it with recognizable garbage) 00803 wipeWithPattern(au, unusedPattern); 00804 00805 // calloc() expects the reported memory address range to be filled with 0's 00806 memset( au->reportedAddress, 0, au->reportedSize ); 00807 00808 // Validate every single allocated unit in memory 00809 if( alwaysValidateAll ) 00810 validateAllAllocs(); 00811 00812 // Log the result 00813 if( alwaysLogAll ) 00814 log("[+] ----> addr 0x%08X", (size_t) au->reportedAddress); 00815 00816 // Resetting the globals insures that if at some later time, somebody 00817 // calls our memory manager from an unknown source (i.e. they didn't 00818 // include our H file) then we won't think it was the last allocation. 00819 resetGlobals(); 00820 00821 // Return the (reported) address of the new allocation unit 00822 return au->reportedAddress; 00823 } 00824 catch( const char *err ) 00825 { 00826 // Deal with the errors 00827 00828 log("[!] %s", err); 00829 resetGlobals(); 00830 00831 return NULL; 00832 } 00833 } 00834 00837 void * MemoryManager::rllocMem( 00838 const char *sourceFile, 00839 const unsigned int sourceLine, 00840 const char *sourceFunc, 00841 const unsigned int reallocationType, 00842 const size_t reportedSize, 00843 void *reportedAddress, 00844 const unsigned processID ) 00845 { 00846 // If we don't have a process ID yet, get one now 00847 if( !gProcessID ) 00848 gProcessID = sMemManager._getProcessID(); 00849 00850 try 00851 { 00852 // ANSI says: Calling realloc with a NULL should force same operations 00853 // as a malloc 00854 if( !reportedAddress ) 00855 { 00856 return allocMem(sourceFile, sourceLine, sourceFunc, reallocationType, reportedSize, processID ); 00857 } 00858 00859 // Increase our allocation count 00860 currentAllocationCount++; 00861 00862 // If you hit this assert, you requested a breakpoint on a specific 00863 // allocation count 00864 m_assert( currentAllocationCount != breakOnAllocationCount ); 00865 00866 // Log the request 00867 if( alwaysLogAll ) 00868 log("[~] %05d %8s of size 0x%08X(%08d) by %s", 00869 currentAllocationCount, 00870 allocationTypes[reallocationType], 00871 reportedSize, 00872 reportedSize, 00873 ownerString(sourceFile, sourceLine, sourceFunc) ); 00874 00875 // Locate the existing allocation unit 00876 sAllocUnit *au = findAllocUnit( reportedAddress ); 00877 00878 // If you hit this assert, you tried to reallocate RAM that wasn't 00879 // allocated by this memory manager. 00880 m_assert(au != NULL); 00881 if( au == NULL ) 00882 throw "Request to reallocate RAM that was never allocated"; 00883 00884 // If you hit this assert, then the allocation unit that is about to be 00885 // reallocated is damaged. But you probably already know that from a 00886 // previous assert you should have seen in validateAllocUnit() :) 00887 m_assert( validateAlloc( au ) ); 00888 00889 // If you hit this assert, then this reallocation was made from a source 00890 // that isn't setup to use this memory tracking software, use the stack 00891 // frame to locate the source and include our H file. 00892 m_assert( reallocationType != m_alloc_unknown ); 00893 if( reallocationType == m_alloc_unknown ) 00894 { 00895 log( "[!] Allocationfrom outside memory tracker in %s(%d)::%s :", sourceFile, sourceLine, sourceFunc ); 00896 dumpAllocUnit( au, " " ); 00897 } 00898 00899 // If you hit this assert, you were trying to reallocate RAM that was 00900 // not allocated in a way that is compatible with realloc. In other 00901 // words, you have a allocation/reallocation mismatch. 00902 m_assert( 00903 au->allocationType == m_alloc_malloc || 00904 au->allocationType == m_alloc_calloc || 00905 au->allocationType == m_alloc_realloc); 00906 if( reallocationType == m_alloc_unknown ) 00907 { 00908 log( "[!] Allocation-deallocation mismatch in %s(%d)::%s :", sourceFile, sourceLine, sourceFunc ); 00909 dumpAllocUnit( au, " " ); 00910 } 00911 00912 // If you hit this assert, then the "break on realloc" flag for this 00913 // allocation unit is set (and will continue to be set until you 00914 // specifically shut it off. Interrogate the 'au' variable to determine 00915 // information about this allocation unit. 00916 m_assert( au->breakOnRealloc == false ); 00917 00918 // Keep track of the original size 00919 size_t originalReportedSize = au->reportedSize; 00920 00921 if (alwaysLogAll) log("[~] ----> from 0x%08X(%08d)", 00922 originalReportedSize, 00923 originalReportedSize); 00924 00925 // Do the reallocation 00926 void *oldReportedAddress = reportedAddress; 00927 size_t newActualSize = calculateActualSize(reportedSize); 00928 void *newActualAddress = NULL; 00929 00930 #ifdef RANDOM_FAILURE 00931 00932 double a = rand(); 00933 double b = RAND_MAX / 100.0 * RANDOM_FAILURE; 00934 if (a > b) 00935 { 00936 newActualAddress = realloc(au->actualAddress, newActualSize); 00937 } 00938 else 00939 { 00940 log("[F] Random faiure"); 00941 } 00942 00943 #else 00944 00945 newActualAddress = realloc(au->actualAddress, newActualSize); 00946 00947 #endif 00948 00949 // We don't want to assert with random failures, because we want the 00950 // application to deal with them. 00951 00952 #ifndef RANDOM_FAILURE 00953 // If you hit this assert, then the requested allocation simply failed 00954 // (you're out of memory) Interrogate the variable 'au' to see the 00955 // original allocation. You can also query 'newActualSize' to see the 00956 // amount of memory trying to be allocated. Finally, you can query 00957 // 'reportedSize' to see how much memory was requested by the caller. 00958 m_assert(newActualAddress); 00959 #endif 00960 00961 if (!newActualAddress) 00962 throw "Request for reallocation failed. Out of memory."; 00963 00964 // Remove this allocation from our stats (we'll add the new reallocation again later) 00965 stats.totalReportedMemory -= au->reportedSize; 00966 stats.totalActualMemory -= au->actualSize; 00967 00968 // Update the allocation with the new information 00969 00970 au->actualSize = newActualSize; 00971 au->actualAddress = newActualAddress; 00972 au->reportedSize = calculateReportedSize(newActualSize); 00973 au->reportedAddress = calculateReportedAddress(newActualAddress); 00974 au->allocationType = reallocationType; 00975 au->sourceLine = sourceLine; 00976 au->allocationNumber = currentAllocationCount; 00977 au->processID = processID; 00978 if( sourceFile ) 00979 strncpy(au->sourceFile, sourceFileStripper(sourceFile), sizeof(au->sourceFile) - 1); 00980 else 00981 strcpy( au->sourceFile, "??" ); 00982 if( sourceFunc ) 00983 strncpy( au->sourceFunc, sourceFunc, sizeof(au->sourceFunc) - 1 ); 00984 else 00985 strcpy( au->sourceFunc, "??" ); 00986 00987 // The reallocation may cause the address to change, so we should 00988 // relocate our allocation unit within the hash table 00989 00990 size_t hashIndex = (unsigned int) -1; 00991 if( oldReportedAddress != au->reportedAddress ) 00992 { 00993 // Remove this allocation unit from the hash table 00994 { 00995 size_t hashIndex = ((size_t) oldReportedAddress >> 4) & (hashSize - 1); 00996 if( hashTable[hashIndex] == au ) 00997 { 00998 hashTable[hashIndex] = hashTable[hashIndex]->next; 00999 } 01000 else 01001 { 01002 if (au->prev) 01003 au->prev->next = au->next; 01004 if (au->next) 01005 au->next->prev = au->prev; 01006 } 01007 } 01008 01009 // Re-insert it back into the hash table 01010 hashIndex = ((size_t) au->reportedAddress >> 4) & (hashSize - 1); 01011 if (hashTable[hashIndex]) 01012 hashTable[hashIndex]->prev = au; 01013 au->next = hashTable[hashIndex]; 01014 au->prev = NULL; 01015 hashTable[hashIndex] = au; 01016 } 01017 01018 // Account for the new allocatin unit in our stats 01019 stats.totalReportedMemory += au->reportedSize; 01020 stats.totalActualMemory += au->actualSize; 01021 if (stats.totalReportedMemory > stats.peakReportedMemory) 01022 stats.peakReportedMemory = stats.totalReportedMemory; 01023 if (stats.totalActualMemory > stats.peakActualMemory) 01024 stats.peakActualMemory = stats.totalActualMemory; 01025 size_t deltaReportedSize = reportedSize - originalReportedSize; 01026 if( deltaReportedSize > 0 ) 01027 { 01028 stats.accumulatedReportedMemory += deltaReportedSize; 01029 stats.accumulatedActualMemory += deltaReportedSize; 01030 } 01031 01032 // Prepare the allocation unit for use (wipe it with recognizable 01033 // garbage) 01034 wipeWithPattern( au, unusedPattern, originalReportedSize ); 01035 01036 // If you hit this assert, then something went wrong, because the 01037 // allocation unit was properly validated PRIOR to the reallocation. 01038 // This should not happen. 01039 m_assert( validateAlloc(au) ); 01040 01041 // Validate every single allocated unit in memory 01042 if( alwaysValidateAll ) 01043 validateAllAllocs(); 01044 01045 // Log the result 01046 if (alwaysLogAll) log("[~] ----> addr 0x%08X", 01047 (size_t) au->reportedAddress); 01048 01049 // Resetting the globals insures that if at some later time, somebody 01050 // calls our memory manager from an unknown source (i.e. they didn't 01051 // include our H file) then we won't think it was the last allocation. 01052 resetGlobals(); 01053 01054 // Return the (reported) address of the new allocation unit 01055 return au->reportedAddress; 01056 } 01057 catch(const char *err) 01058 { 01059 // Deal with the errors 01060 log("[!] %s", err); 01061 resetGlobals(); 01062 01063 return NULL; 01064 } 01065 } 01066 01067 // --------------------------------------------------------------------------------------------------------------------------------- 01068 // Deallocate memory and track it 01069 // --------------------------------------------------------------------------------------------------------------------------------- 01070 01071 void MemoryManager::dllocMem(const char *sourceFile, const unsigned int sourceLine, const char *sourceFunc, const unsigned int deallocationType, const void *reportedAddress, const unsigned processID ) 01072 { 01073 try 01074 { 01075 // Log the request 01076 if (alwaysLogAll) log("[-] ----- %8s of addr 0x%08X by %s", 01077 allocationTypes[deallocationType], 01078 (size_t) reportedAddress, 01079 ownerString(sourceFile, sourceLine, sourceFunc) ); 01080 01081 // Go get the allocation unit 01082 01083 sAllocUnit *au = findAllocUnit( reportedAddress ); 01084 01085 // If you hit this assert, you tried to deallocate RAM that wasn't 01086 // allocated by this memory manager. 01087 m_assert(au != NULL); 01088 if (au == NULL) 01089 throw "Request to deallocate RAM that was never allocated"; 01090 01091 // If you hit this assert, then the allocation unit that is about to be 01092 // deallocated is damaged. But you probably already know that from a 01093 // previous assert you should have seen in validateAllocUnit() :) 01094 m_assert(validateAlloc(au)); 01095 01096 // If you hit this assert, then this deallocation was made from a 01097 // source that isn't setup to use this memory tracking software, use 01098 // the stack frame to locate the source and include our H file. 01099 m_assert(deallocationType != m_alloc_unknown); 01100 if( deallocationType == m_alloc_unknown ) 01101 { 01102 log( "[!] Allocation-deallocation mismatch in %s(%d)::%s :", sourceFile, sourceLine, sourceFunc ); 01103 dumpAllocUnit( au, " " ); 01104 } 01105 01106 // If you hit this assert, you were trying to deallocate RAM that was 01107 // not allocated in a way that is compatible with the deallocation 01108 // method requested. In other words, you have a allocation/deallocation 01109 // mismatch. 01110 m_assert( 01111 (deallocationType == m_alloc_delete && au->allocationType == m_alloc_new ) || 01112 (deallocationType == m_alloc_delete_array && au->allocationType == m_alloc_new_array) || 01113 (deallocationType == m_alloc_free && au->allocationType == m_alloc_malloc ) || 01114 (deallocationType == m_alloc_free && au->allocationType == m_alloc_calloc ) || 01115 (deallocationType == m_alloc_free && au->allocationType == m_alloc_realloc ) || 01116 (deallocationType == m_alloc_unknown ) ); 01117 if( 01118 !( 01119 (deallocationType == m_alloc_delete && au->allocationType == m_alloc_new ) || 01120 (deallocationType == m_alloc_delete_array && au->allocationType == m_alloc_new_array) || 01121 (deallocationType == m_alloc_free && au->allocationType == m_alloc_malloc ) || 01122 (deallocationType == m_alloc_free && au->allocationType == m_alloc_calloc ) || 01123 (deallocationType == m_alloc_free && au->allocationType == m_alloc_realloc ) || 01124 (deallocationType == m_alloc_unknown ) ) ) 01125 { 01126 log( "[!] Allocation-deallocation mismatch in %s(%d)::%s :", sourceFile, sourceLine, sourceFunc ); 01127 dumpAllocUnit( au, " " ); 01128 } 01129 01130 // If you hit this assert, then this deallocation was made from another 01131 // process than the one in which the allocation was made. 01132 // m_assert( au->processID == processID ); 01133 01134 // If you hit this assert, then the "break on dealloc" flag for this 01135 // allocation unit is set. Interrogate the 'au' 01136 // variable to determine information about this allocation unit. 01137 m_assert(au->breakOnDealloc == false); 01138 01139 // Wipe the deallocated RAM with a new pattern. This doen't actually do 01140 // us much good in debug mode under WIN32, because Microsoft's memory 01141 // debugging & tracking utilities will wipe it right after we do. 01142 // Oh well. 01143 wipeWithPattern( au, releasedPattern ); 01144 01145 // Do the deallocation 01146 free(au->actualAddress); 01147 01148 // Remove this allocation unit from the hash table 01149 size_t hashIndex = ((size_t) au->reportedAddress >> 4) & (hashSize - 1); 01150 if( hashTable[hashIndex] == au ) 01151 { 01152 hashTable[hashIndex] = au->next; 01153 } 01154 else 01155 { 01156 if (au->prev) 01157 au->prev->next = au->next; 01158 if (au->next) 01159 au->next->prev = au->prev; 01160 } 01161 01162 // Remove this allocation from our stats 01163 stats.totalReportedMemory -= au->reportedSize; 01164 stats.totalActualMemory -= au->actualSize; 01165 stats.totalAllocUnitCount--; 01166 01167 // Add this allocation unit to the front of our reservoir of unused allocation units 01168 memset( au, 0, sizeof(sAllocUnit) ); 01169 au->next = reservoir; 01170 reservoir = au; 01171 01172 // Resetting the globals insures that if at some later time, somebody 01173 // calls our memory manager from an unknown source (i.e. they didn't 01174 // include our H file) then we won't think it was the last allocation. 01175 resetGlobals(); 01176 01177 // Validate every single allocated unit in memory 01178 if( alwaysValidateAll ) 01179 validateAllAllocs(); 01180 01181 // If we're in the midst of deinitialization time, track any pending memory leaks 01182 if( m_bDeinitTime ) 01183 dumpLeakReport(); 01184 } 01185 catch(const char *err) 01186 { 01187 // Deal with errors 01188 log("[!] %s", err); 01189 resetGlobals(); 01190 } 01191 } 01192 01196 bool MemoryManager::validateAddr( const void *reportedAddress ) 01197 { 01198 // Just see if the address exists in our allocation routines 01199 return findAllocUnit(reportedAddress) != NULL; 01200 } 01201 01202 bool MemoryManager::validateAlloc( const sAllocUnit *allocUnit ) 01203 { 01204 // Make sure the padding is untouched 01205 long *pre = (long *)allocUnit->actualAddress; 01206 long *post = (long *)((char *)allocUnit->actualAddress + allocUnit->actualSize - paddingSize * sizeof(long)); 01207 bool errorFlag = false; 01208 for( unsigned int i = 0; i < paddingSize; i++, pre++, post++ ) 01209 { 01210 if( *pre != (long)prefixPattern ) 01211 { 01212 log("[!] A memory allocation unit was corrupt because of an underrun:"); 01213 dumpAllocUnit( allocUnit, " " ); 01214 errorFlag = true; 01215 } 01216 01217 // If you hit this assert, then you should know that this allocation 01218 // unit has been damaged. Something (possibly the owner?) has underrun 01219 // the allocation unit (modified a few bytes prior to the start). You 01220 // can interrogate the variable 'allocUnit' to see statistics and 01221 // information about this damaged allocation unit. 01222 m_assert(*pre == (long) prefixPattern); 01223 01224 if (*post != (long) postfixPattern) 01225 { 01226 log("[!] A memory allocation unit was corrupt because of an overrun:"); 01227 dumpAllocUnit(allocUnit, " "); 01228 errorFlag = true; 01229 } 01230 01231 // If you hit this assert, then you should know that this allocation 01232 // unit has been damaged. Something (possibly the owner?) has overrun 01233 // the allocation unit (modified a few bytes after the end). You can 01234 // interrogate the variable 'allocUnit' to see statistics and 01235 // information about this damaged allocation unit. 01236 m_assert(*post == (long) postfixPattern); 01237 } 01238 01239 // Return the error status (we invert it, because a return of 'false' means error) 01240 return !errorFlag; 01241 } 01242 01243 bool MemoryManager::validateAllAllocs() 01244 { 01245 // Just go through each allocation unit in the hash table and count the ones that have errors 01246 unsigned int errors = 0; 01247 unsigned int allocCount = 0; 01248 01249 for( unsigned int i = 0; i < hashSize; i++ ) 01250 { 01251 sAllocUnit *ptr = hashTable[i]; 01252 while(ptr) 01253 { 01254 allocCount++; 01255 if (!validateAlloc(ptr)) 01256 errors++; 01257 ptr = ptr->next; 01258 } 01259 } 01260 01261 // Test for hash-table correctness 01262 if( allocCount != stats.totalAllocUnitCount ) 01263 { 01264 log("[!] Memory tracking hash table corrupt!"); 01265 errors++; 01266 } 01267 01268 // If you hit this assert, then the internal memory (hash table) used by 01269 // this memory tracking software is damaged! The best way to track this 01270 // down is to use the alwaysLogAll flag in conjunction with STRESS_TEST 01271 // macro to narrow in on the offending code. After running the application 01272 // with these settings (and hitting this assert again), interrogate the 01273 // memory.log file to find the previous successful operation. The 01274 // corruption will have occurred between that point and this assertion. 01275 m_assert( allocCount == stats.totalAllocUnitCount ); 01276 01277 // If you hit this assert, then you've probably already been notified that 01278 // there was a problem with a allocation unit in a prior call to 01279 // validateAllocUnit(), but this assert is here just to make sure you know 01280 // about it. :) 01281 m_assert( errors == 0 ); 01282 01283 // Log any errors 01284 if (errors) 01285 log("[!] While validating all allocation units, %d allocation unit(s) were found to have problems", 01286 errors ); 01287 01288 // Return the error status 01289 return errors != 0; 01290 } 01291 01294 unsigned int MemoryManager::calcUnused( const sAllocUnit *allocUnit ) 01295 { 01296 const unsigned long *ptr = (const unsigned long *)allocUnit->reportedAddress; 01297 unsigned int count = 0; 01298 01299 for( unsigned int i = 0; i < allocUnit->reportedSize; i += sizeof(long), ptr++ ) 01300 { 01301 if (*ptr == unusedPattern) count += sizeof(long); 01302 } 01303 01304 return count; 01305 } 01306 01307 unsigned int MemoryManager::calcAllUnused() 01308 { 01309 // Just go through each allocation unit in the hash table and count the 01310 // unused RAM 01311 unsigned int total = 0; 01312 for( unsigned int i = 0; i < hashSize; i++ ) 01313 { 01314 sAllocUnit *ptr = hashTable[i]; 01315 while(ptr) 01316 { 01317 total += calcUnused(ptr); 01318 ptr = ptr->next; 01319 } 01320 } 01321 01322 return total; 01323 } 01324 01325 void MemoryManager::dumpAllocUnit(const sAllocUnit *allocUnit, const char *prefix) 01326 { 01327 log("[I] %sAddress (reported): %010p", prefix, allocUnit->reportedAddress); 01328 log("[I] %sAddress (actual) : %010p", prefix, allocUnit->actualAddress); 01329 log("[I] %sSize (reported) : 0x%08X (%s)", prefix, allocUnit->reportedSize, 01330 memorySizeString(allocUnit->reportedSize)); 01331 log("[I] %sSize (actual) : 0x%08X (%s)", prefix, allocUnit->actualSize, 01332 memorySizeString(allocUnit->actualSize)); 01333 log("[I] %sOwner : %s(%d)::%s", prefix, allocUnit->sourceFile, allocUnit->sourceLine, allocUnit->sourceFunc); 01334 log("[I] %sAllocation type : %s", prefix, allocationTypes[allocUnit->allocationType]); 01335 log("[I] %sAllocation number : %d", prefix, allocUnit->allocationNumber); 01336 } 01337 01338 void MemoryManager::dumpMemReport(const char *filename, const bool overwrite) 01339 { 01340 // Open the report file 01341 FILE *fp = NULL; 01342 01343 if (overwrite) 01344 fp = fopen(filename, "w+b"); 01345 else 01346 fp = fopen(filename, "ab"); 01347 01348 // If you hit this assert, then the memory report generator is unable to 01349 // log information to a file (can't open the file for some reason.) 01350 m_assert(fp); 01351 if (!fp) 01352 return; 01353 01354 // Header 01355 char timeString[25]; 01356 memset(timeString, 0, sizeof(timeString)); 01357 time_t t = time(NULL); 01358 struct tm *tme = localtime(&t); 01359 01360 fprintf(fp, " ---------------------------------------------------------------------------------------------------------------------------------- \r\n"); 01361 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); 01362 fprintf(fp, " ---------------------------------------------------------------------------------------------------------------------------------- \r\n"); 01363 fprintf(fp, "\r\n"); 01364 fprintf(fp, "\r\n"); 01365 01366 // Report summary 01367 fprintf(fp, " ---------------------------------------------------------------------------------------------------------------------------------- \r\n"); 01368 fprintf(fp, "| T O T A L S |\r\n"); 01369 fprintf(fp, " ---------------------------------------------------------------------------------------------------------------------------------- \r\n"); 01370 fprintf(fp, " Allocation unit count: %10s\r\n", insertCommas(stats.totalAllocUnitCount)); 01371 fprintf(fp, " Reported to application: %s\r\n", memorySizeString(stats.totalReportedMemory)); 01372 fprintf(fp, " Actual total memory in use: %s\r\n", memorySizeString(stats.totalActualMemory)); 01373 fprintf(fp, " Memory tracking overhead: %s\r\n", memorySizeString(stats.totalActualMemory - stats.totalReportedMemory)); 01374 fprintf(fp, "\r\n"); 01375 01376 fprintf(fp, " ---------------------------------------------------------------------------------------------------------------------------------- \r\n"); 01377 fprintf(fp, "| P E A K S |\r\n"); 01378 fprintf(fp, " ---------------------------------------------------------------------------------------------------------------------------------- \r\n"); 01379 fprintf(fp, " Allocation unit count: %10s\r\n", insertCommas(stats.peakAllocUnitCount)); 01380 fprintf(fp, " Reported to application: %s\r\n", memorySizeString(stats.peakReportedMemory)); 01381 fprintf(fp, " Actual: %s\r\n", memorySizeString(stats.peakActualMemory)); 01382 fprintf(fp, " Memory tracking overhead: %s\r\n", memorySizeString(stats.peakActualMemory - stats.peakReportedMemory)); 01383 fprintf(fp, "\r\n"); 01384 01385 fprintf(fp, " ---------------------------------------------------------------------------------------------------------------------------------- \r\n"); 01386 fprintf(fp, "| A C C U M U L A T E D |\r\n"); 01387 fprintf(fp, " ---------------------------------------------------------------------------------------------------------------------------------- \r\n"); 01388 fprintf(fp, " Allocation unit count: %s\r\n", memorySizeString(stats.accumulatedAllocUnitCount)); 01389 fprintf(fp, " Reported to application: %s\r\n", memorySizeString(stats.accumulatedReportedMemory)); 01390 fprintf(fp, " Actual: %s\r\n", memorySizeString(stats.accumulatedActualMemory)); 01391 fprintf(fp, "\r\n"); 01392 01393 fprintf(fp, " ---------------------------------------------------------------------------------------------------------------------------------- \r\n"); 01394 fprintf(fp, "| U N U S E D |\r\n"); 01395 fprintf(fp, " ---------------------------------------------------------------------------------------------------------------------------------- \r\n"); 01396 fprintf(fp, " Memory allocated but not in use: %s\r\n", memorySizeString(calcAllUnused())); 01397 fprintf(fp, "\r\n"); 01398 01399 dumpAllocations(fp); 01400 01401 fclose(fp); 01402 } 01403 01404 sMStats MemoryManager::getMemStats() 01405 { 01406 return stats; 01407 } 01408 01409 // --------------------------------------------------------------------------------------------------------------------------------- 01410 // Global new/new[] 01411 // 01412 // These are the standard new/new[] operators. They are merely interface functions that operate like normal new/new[], but use our 01413 // memory tracking routines. 01414 // --------------------------------------------------------------------------------------------------------------------------------- 01415 void *MemoryManager::op_new_sc( size_t reportedSize, unsigned processID ) 01416 { 01417 // Save these off... 01418 const char *file = sourceFile; 01419 const unsigned int line = sourceLine; 01420 const char *func = sourceFunc; 01421 01422 // ANSI says: allocation requests of 0 bytes will still return a valid value 01423 if (reportedSize == 0) reportedSize = 1; 01424 01425 // ANSI says: loop continuously because the error handler could possibly free up some memory 01426 for(;;) 01427 { 01428 // Try the allocation 01429 void *ptr = MemoryManager::sMemManager.allocMem(file, line, func, m_alloc_new, reportedSize, processID ); 01430 if( ptr ) 01431 { 01432 return ptr; 01433 } 01434 01435 // There isn't a way to determine the new handler, except through setting it. So we'll just set it to NULL, then 01436 // set it back again. 01437 std::new_handler nh = std::set_new_handler(0); 01438 std::set_new_handler(nh); 01439 01440 // If there is an error handler, call it 01441 if (nh) 01442 { 01443 (*nh)(); 01444 } 01445 01446 // Otherwise, throw the exception 01447 else 01448 { 01449 throw std::bad_alloc(); 01450 } 01451 } 01452 } 01453 01454 // --------------------------------------------------------------------------------------------------------------------------------- 01455 void *MemoryManager::op_new_vc( size_t reportedSize, unsigned processID ) 01456 { 01457 // Save these off... 01458 const char *file = sourceFile; 01459 const unsigned int line = sourceLine; 01460 const char *func = sourceFunc; 01461 01462 // The ANSI standard says that allocation requests of 0 bytes will still return a valid value 01463 if (reportedSize == 0) reportedSize = 1; 01464 01465 // ANSI says: loop continuously because the error handler could possibly free up some memory 01466 for(;;) 01467 { 01468 // Try the allocation 01469 void *ptr = MemoryManager::sMemManager.allocMem(file, line, func, m_alloc_new_array, reportedSize, processID ); 01470 if( ptr ) 01471 { 01472 return ptr; 01473 } 01474 01475 // There isn't a way to determine the new handler, except through setting it. So we'll just set it to NULL, then 01476 // set it back again. 01477 std::new_handler nh = std::set_new_handler(0); 01478 std::set_new_handler(nh); 01479 01480 // If there is an error handler, call it 01481 if (nh) 01482 { 01483 (*nh)(); 01484 } 01485 01486 // Otherwise, throw the exception 01487 else 01488 { 01489 throw std::bad_alloc(); 01490 } 01491 } 01492 } 01493 01494 // --------------------------------------------------------------------------------------------------------------------------------- 01495 // Other global new/new[] 01496 // 01497 // These are the standard new/new[] operators as used by Microsoft's memory tracker. We don't want them interfering with our memory 01498 // tracking efforts. Like the previous versions, these are merely interface functions that operate like normal new/new[], but use 01499 // our memory tracking routines. 01500 // --------------------------------------------------------------------------------------------------------------------------------- 01501 void *MemoryManager::op_new_sc( size_t reportedSize, const char *sourceFile, int sourceLine, unsigned processID ) 01502 { 01503 // The ANSI standard says that allocation requests of 0 bytes will still 01504 // return a valid value 01505 if( reportedSize == 0 ) 01506 reportedSize = 1; 01507 01508 // ANSI says: loop continuously because the error handler could possibly 01509 // free up some memory 01510 for(;;) 01511 { 01512 // Try the allocation 01513 01514 void *ptr = MemoryManager::sMemManager.allocMem(sourceFile, sourceLine, "??", m_alloc_new, reportedSize, processID ); 01515 if (ptr) 01516 { 01517 return ptr; 01518 } 01519 01520 // There isn't a way to determine the new handler, except through setting it. So we'll just set it to NULL, then 01521 // set it back again. 01522 01523 std::new_handler nh = std::set_new_handler(0); 01524 std::set_new_handler(nh); 01525 01526 // If there is an error handler, call it 01527 01528 if (nh) 01529 { 01530 (*nh)(); 01531 } 01532 01533 // Otherwise, throw the exception 01534 01535 else 01536 { 01537 throw std::bad_alloc(); 01538 } 01539 } 01540 } 01541 01542 // --------------------------------------------------------------------------------------------------------------------------------- 01543 void *MemoryManager::op_new_vc(size_t reportedSize, const char *sourceFile, int sourceLine, unsigned processID ) 01544 { 01545 // ANSI says : allocation requests of 0 bytes will still return a valid 01546 // value 01547 if( reportedSize == 0 ) 01548 reportedSize = 1; 01549 01550 // ANSI says: loop continuously because the error handler could possibly 01551 // free up some memory 01552 01553 for(;;) 01554 { 01555 // Try the allocation 01556 void *ptr = MemoryManager::sMemManager.allocMem( 01557 sourceFile, 01558 sourceLine, 01559 "??", 01560 m_alloc_new_array, 01561 reportedSize, 01562 processID ); 01563 if( ptr ) 01564 { 01565 return ptr; 01566 } 01567 01568 // There isn't a way to determine the new handler, except through 01569 // setting it. So we'll just set it to NULL, then set it back again. 01570 std::new_handler nh = std::set_new_handler(0); 01571 std::set_new_handler(nh); 01572 01573 // If there is an error handler, call it 01574 if( nh ) 01575 { 01576 (*nh)(); 01577 } 01578 01579 // Otherwise, throw the exception 01580 else 01581 { 01582 throw std::bad_alloc(); 01583 } 01584 } 01585 } 01586 01587 // --------------------------------------------------------------------------------------------------------------------------------- 01588 // Global delete/delete[] 01589 // 01590 // These are the standard delete/delete[] operators. They are merely interface functions that operate like normal delete/delete[], 01591 // but use our memory tracking routines. 01592 // --------------------------------------------------------------------------------------------------------------------------------- 01593 void MemoryManager::op_del_sc(void *reportedAddress, unsigned processID ) 01594 { 01595 // ANSI says: delete & delete[] allow NULL pointers (they do nothing) 01596 if( reportedAddress ) 01597 MemoryManager::sMemManager.dllocMem(sourceFile, sourceLine, sourceFunc, m_alloc_delete, reportedAddress, processID ); 01598 else if( alwaysLogAll ) 01599 log("[-] ----- %8s of NULL by %s", 01600 allocationTypes[m_alloc_delete], 01601 ownerString(sourceFile, sourceLine, sourceFunc) ); 01602 01603 // Resetting the globals insures that if at some later time, somebody calls 01604 // our memory manager from an unknown source (i.e. they didn't include our 01605 // H file) then we won't think it was the last allocation. 01606 resetGlobals(); 01607 } 01608 01609 // --------------------------------------------------------------------------------------------------------------------------------- 01610 void MemoryManager::op_del_vc(void *reportedAddress, unsigned processID ) 01611 { 01612 // ANSI says: delete & delete[] allow NULL pointers (they do nothing) 01613 if (reportedAddress) 01614 MemoryManager::sMemManager.dllocMem( 01615 sourceFile, 01616 sourceLine, 01617 sourceFunc, 01618 m_alloc_delete_array, 01619 reportedAddress, 01620 processID ); 01621 else if( alwaysLogAll ) 01622 log("[-] ----- %8s of NULL by %s", allocationTypes[m_alloc_delete_array], ownerString(sourceFile, sourceLine, sourceFunc)); 01623 01624 // Resetting the globals insures that if at some later time, somebody calls 01625 // our memory manager from an unknown source (i.e. they didn't include our 01626 // H file) then we won't think it was the last allocation. 01627 resetGlobals(); 01628 } 01629 01630 MemoryManager::MemoryManager() 01631 : m_uProcessIDs( 0 ), m_bDeinitTime( false ) 01632 { 01633 doCleanupLogOnFirstRun(); 01634 } 01635 01636 MemoryManager::~MemoryManager() 01637 { 01638 m_bDeinitTime = true; 01639 dumpLeakReport(); 01640 } 01641 01642 unsigned MemoryManager::_getProcessID() 01643 { 01644 return ++m_uProcessIDs; 01645 } 01646 01647 #else 01648 01649 //----------------------------------------------------------------------------- 01650 MemoryManager::MemoryManager() 01651 { 01652 } 01653 01654 //----------------------------------------------------------------------------- 01655 MemoryManager::~MemoryManager() 01656 { 01657 } 01658 01659 //----------------------------------------------------------------------------- 01660 void * MemoryManager::allocMem( const char *szFile, size_t uLine, 01661 size_t count ) throw() 01662 { 01663 void *ptr = malloc( count ); 01664 return ptr; 01665 } 01666 01667 //----------------------------------------------------------------------------- 01668 void * MemoryManager::rllocMem( 01669 const char *szFile, size_t uLine, void *ptr , size_t count ) throw() 01670 { 01671 void *nptr = realloc( ptr, count ); 01672 return nptr; 01673 } 01674 01675 //----------------------------------------------------------------------------- 01676 void * MemoryManager::cllocMem( 01677 const char *szFile, size_t uLine, size_t num, size_t size ) throw() 01678 { 01679 void *ptr = malloc( num * size ); 01680 01681 if( ptr ) 01682 { 01683 memset( ptr , 0, num * size ); 01684 } 01685 return ptr; 01686 } 01687 01688 //----------------------------------------------------------------------------- 01689 void MemoryManager::dllocMem( const char *szFile, size_t uLine, 01690 void *ptr) throw() 01691 { 01692 free( ptr ); 01693 } 01694 01695 #endif // OGRE_DEBUG_MEMORY_MANAGER 01696 01697 } 01698
Copyright © 2002-2003 by The OGRE Team
Last modified Sun Nov 28 19:48:34 2004