Main Page   Namespace List   Class Hierarchy   Alphabetical List   Compound List   File List   Namespace Members   Compound Members   File Members   Related Pages  

OgreMemoryManager.cpp

Go to the documentation of this file.
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 Fri May 14 23:22:25 2004