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 Sun Nov 28 19:48:34 2004