Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

source: downloads/OgreMain/src/OgreMemoryManager.cpp @ 3

Last change on this file since 3 was 3, checked in by anonymous, 17 years ago

=update

File size: 70.8 KB
Line 
1/*-------------------------------------------------------------------------
2This source file is a part of OGRE
3(Object-oriented Graphics Rendering Engine)
4
5For the latest info, see http://www.ogre3d.org/
6
7Copyright (c) 2000-2006 Torus Knot Software Ltd
8Also see acknowledgements in Readme.html
9
10This library is free software; you can redistribute it and/or modify it
11under the terms of the GNU Lesser General Public License (LGPL) as
12published by the Free Software Foundation; either version 2.1 of the
13License, or (at your option) any later version.
14
15This library is distributed in the hope that it will be useful, but
16WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
17or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public
18License for more details.
19
20You should have received a copy of the GNU Lesser General Public License
21along with this library; if not, write to the Free Software Foundation,
22Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA or go to
23http://www.gnu.org/copyleft/lesser.txt
24-------------------------------------------------------------------------*/
25#include "OgreStableHeaders.h"
26//---- ORIGINAL COPYRIGHT FOLLOWS -------------------------------------------
27// ---------------------------------------------------------------------------------------------------------------------------------
28// Copyright 2000, Paul Nettle. All rights reserved.
29//
30// You are free to use this source code in any commercial or non-commercial product.
31//
32// mmgr.cpp - Memory manager & tracking software
33//
34// The most recent version of this software can be found at: ftp://ftp.GraphicsPapers.com/pub/ProgrammingTools/MemoryManagers/
35//
36// [NOTE: Best when viewed with 8-character tabs]
37//
38// ---------------------------------------------------------------------------------------------------------------------------------
39
40#include "OgreMemoryManager.h"
41
42//-----------------------------------------------------------------------------
43// Allow the use of the real *alloc/free/new/delete functions
44#include "OgreNoMemoryMacros.h"
45//-----------------------------------------------------------------------------
46
47namespace Ogre
48{
49
50    //-----------------------------------------------------------------------------
51    MemoryManager& MemoryManager::instance(void)
52    {
53        // Ensure MemoryManager construct before any memory allocate,
54        // and then destruct after all memory deallocated.
55        static MemoryManager sMemManager;
56
57        return sMemManager;
58    }
59    //-----------------------------------------------------------------------------
60
61#if OGRE_DEBUG_MEMORY_MANAGER && OGRE_DEBUG_MODE
62
63#define OGRE_MEMMANAGER_STRESS_TEST 0
64
65#if OGRE_MEMORY_STRESS_TEST
66
67    bool randomWipe           = true;
68    bool alwaysValidateAll    = true;
69    bool alwaysLogAll         = true;
70    bool alwaysWipeAll        = false;
71    bool cleanupLogOnFirstRun = true;
72
73    const unsigned int hashBits    = 24;
74    const unsigned int paddingSize = 1024; // An extra 8K per allocation!
75
76#else
77
78    bool randomWipe           = false;
79    bool alwaysValidateAll    = false;
80    bool alwaysLogAll         = false;
81    bool alwaysWipeAll        = true;
82    bool cleanupLogOnFirstRun = true;
83
84    const unsigned int hashBits    = 24;
85    const unsigned int paddingSize = 4;
86
87#endif
88
89    //---------------------------------------------------------------------------------------------
90    // We define our own assert, because we don't want to bring up an assertion dialog, since that
91    // allocates RAM. Our new assert simply declares a forced breakpoint.
92    //
93    // The BEOS assert added by Arvid Norberg <arvid@iname.com>.   
94    #ifdef    WIN32
95        #ifdef    _DEBUG
96            #if defined(_MSC_VER)
97                    #define m_assert(x)  if( (x) == false ) __debugbreak();               
98            #elif defined(__GNUC__)
99                #define m_assert(x)  if( (x) == false ) __asm ("int $3"); 
100            #endif
101        #else
102            #define    m_assert(x)
103        #endif
104    #elif defined(__BEOS__)
105        #ifdef DEBUG
106    extern void debugger(const char *message);
107            #define m_assert(x) { if( (x) == false ) debugger("mmgr: assert failed") }
108        #else
109            #define m_assert(x)
110        #endif
111    #else    // We can use this safely on *NIX, since it doesn't bring up a dialog window.
112        #define m_assert(cond) assert(cond)
113    #endif
114    //---------------------------------------------------------------------------------------------
115
116    //---------------------------------------------------------------------------------------------
117    // Here, we turn off our macros because any place in this source file where the word 'new' or
118    // the word 'delete' (etc.) appear will be expanded by the macro. So to avoid problems using
119    // them within this source file, we'll just #undef them.
120    #include "OgreNoMemoryMacros.h"
121    //---------------------------------------------------------------------------------------------
122
123    //---------------------------------------------------------------------------------------------
124    // Get to know these values. They represent the values that will be used to fill unused and
125    // deallocated RAM.   
126    unsigned int prefixPattern   = 0xbaadf00d; // Fill pattern for bytes preceeding allocated blocks
127    unsigned int postfixPattern  = 0xdeadc0de; // Fill pattern for bytes following allocated blocks
128    unsigned int unusedPattern   = 0xfeedface; // Fill pattern for freshly allocated blocks
129    unsigned int releasedPattern = 0xdeadbeef; // Fill pattern for deallocated blocks
130    //---------------------------------------------------------------------------------------------
131
132    //---------------------------------------------------------------------------------------------
133    // Other locals   
134    const unsigned int hashSize = 1 << hashBits;
135    const char *allocationTypes[] = 
136    {
137        "Unknown", 
138        "new",     
139        "new[]", 
140        "malloc",   
141        "calloc", 
142        "realloc", 
143        "delete", 
144        "delete[]", 
145        "free"
146    };
147    //---------------------------------------------------------------------------------------------
148
149    sAllocUnit *hashTable[hashSize];
150    sAllocUnit *reservoir = NULL;
151
152    unsigned int currentAllocationCount = 0;
153    unsigned int breakOnAllocationCount = 0;
154
155    sMStats stats;
156       
157    const char *sourceFile = "??";
158    const char *sourceFunc = "??";
159    unsigned int sourceLine = 0;
160
161    sAllocUnit    **reservoirBuffer      = NULL;
162    unsigned int    reservoirBufferSize    = 0;
163
164    const char *memoryLogFile     = "OgreMemory.log";
165    const char *memoryLeakLogFile = "OgreLeaks.log";
166
167    void doCleanupLogOnFirstRun();
168
169    //---------------------------------------------------------------------------------------------
170    // Local functions only
171    //---------------------------------------------------------------------------------------------
172
173    //---------------------------------------------------------------------------------------------
174    /** Logs a piece of information.
175    */
176    void log( const char *format, ... )
177    {
178        // The buffer
179        char buffer[2048];
180
181        va_list ap;
182        va_start( ap, format );
183        vsprintf( buffer, format, ap );
184        va_end( ap );
185
186        // Cleanup the log?
187
188        if( cleanupLogOnFirstRun )
189            doCleanupLogOnFirstRun();
190
191        // Open the log file
192        FILE *fp = fopen( memoryLogFile, "ab" );
193
194        // If you hit this assert, then the memory logger is unable to log
195        // information to a file (can't open the file for some reason.) You can
196        // interrogate the variable 'buffer' to see what was supposed to be logged
197        // (but won't be.)
198        m_assert(fp);
199
200        if( !fp ) 
201            return;
202
203        // Spit out the data to the log
204        fprintf( fp, "%s\r\n", buffer );
205
206        fclose( fp );
207    }
208
209    //---------------------------------------------------------------------------------------------
210    /** Cleans up the log.
211    */
212    void doCleanupLogOnFirstRun()
213    {
214        if( cleanupLogOnFirstRun )
215        {
216            unlink( memoryLogFile );
217            cleanupLogOnFirstRun = false;
218
219            // Print a header for the log
220            time_t t = time(NULL);
221            log("--------------------------------------------------------------------------------");
222            log("");
223            log("      %s - Memory logging file created on %s", memoryLogFile, asctime(localtime(&t)));
224            log("--------------------------------------------------------------------------------");
225            log("");
226            log("This file contains a log of all memory operations performed during the last run.");
227            log("");
228            log("Interrogate this file to track errors or to help track down memory-related");
229            log("issues. You can do this by tracing the allocations performed by a specific owner");
230            log("or by tracking a specific address through a series of allocations and");
231            log("reallocations.");
232            log("");
233            log("There is a lot of useful information here which, when used creatively, can be");
234            log("extremely helpful.");
235            log("");
236            log("Note that the following guides are used throughout this file:");
237            log("");
238            log("   [!] - Error");
239            log("   [+] - Allocation");
240            log("   [~] - Reallocation");
241            log("   [-] - Deallocation");
242            log("   [I] - Generic information");
243            log("   [F] - Failure induced for the purpose of stress-testing your application");
244            log("   [D] - Information used for debugging this memory manager");
245            log("");
246            log("...so, to find all errors in the file, search for \"[!]\"");
247            log("");
248            log("--------------------------------------------------------------------------------");
249        }
250    }
251
252    //---------------------------------------------------------------------------------------------
253    /** This function strips the path from the beginning of the string that
254        contains a path and a file name.
255        @returns
256            If possible, only the file name. Otherwise, the full string is
257            returned.
258    */
259    const char *sourceFileStripper( const char *sourceFile )
260    {
261        const char *ptr = strrchr(sourceFile, '\\');
262        if( ptr ) 
263            return ptr + 1;
264        ptr = strrchr( sourceFile, '/' );
265        if( ptr ) 
266            return ptr + 1;
267        return sourceFile;
268    }
269
270    //---------------------------------------------------------------------------------------------
271    /** This helper function writes file and line information to a string.
272        @note
273            This function is not thread-safe.
274    */
275    const char *ownerString(
276        const char *sourceFile, 
277        const unsigned int sourceLine, 
278        const char *sourceFunc )
279    {
280        static char str[90];
281#if OGRE_PLATFORM == OGRE_PLATFORM_WIN32 && !defined( __MINGW32__ )
282                _snprintf( 
283#else
284        snprintf( 
285#endif
286                        str, 89, "%s(%05d)::%s", 
287            sourceFileStripper(sourceFile), 
288            sourceLine, 
289            sourceFunc);
290        return str;
291    }
292
293    //---------------------------------------------------------------------------------------------
294    /** This helper function transforms an integer into a string with decimal
295        separators.
296        @note
297            This function is not thread-safe.
298    */
299    const char *insertCommas( size_t value )
300    {
301        static char str[30];
302
303        // This pointer is used to add digits moving backwards in the string.
304        char *p = &str[28];
305        // The current digit
306        int c_digit = 1;
307
308        // Set the last character in the string to NULL.
309        str[29] = 0;
310
311        // While we've still got some digits in value, add them.
312        while( value )
313        {
314            *p-- = '0' + (char)( value % 10 ); value /= 10;
315
316            // If the digit which was inserted was at the end of a group, add a comma.
317            if( !( c_digit % 3 ) )
318                *p-- = ',';
319
320            c_digit++;
321        }
322
323        // Now return the offset in the static string above.
324        return ++p;
325    }
326
327    //---------------------------------------------------------------------------------------------
328    /** Converts a decimal memory value into human-readable format.
329        @note
330            This function is not thread-safe.
331    */
332    const char *memorySizeString( size_t size )
333    {       
334        static char str[90];
335
336        if( size > 1048576 )
337            sprintf( str, "%10s (%7.2fM)", insertCommas( size ), (float) size / 1048576.0f );
338        else if( size > 1024 )       
339            sprintf( str, "%10s (%7.2fK)", insertCommas( size ), (float) size / 1024.0f );
340        else
341            sprintf( str, "%10s bytes     ", insertCommas( size ) );
342        return str;
343    }
344
345    //---------------------------------------------------------------------------------------------
346    /** Tries to locate an allocation unit.
347    */
348    sAllocUnit *findAllocUnit(const void *reportedAddress)
349    {
350        // Just in case...
351        m_assert( reportedAddress != NULL );
352
353        // Use the address to locate the hash index. Note that we shift off the
354        // lower four bits. This is because most allocated addresses will be on
355        // four-, eight- or even sixteen-byte boundaries. If we didn't do this,
356        // the hash index would not have very good coverage.
357
358        size_t hashIndex = ( (size_t)reportedAddress >> 4 ) & ( hashSize - 1 );
359        sAllocUnit *ptr = hashTable[ hashIndex ];
360        while( ptr )
361        {
362            if( ptr->reportedAddress == reportedAddress )
363                return ptr;
364            ptr = ptr->next;
365        }
366
367        return NULL;
368    }
369
370    // ---------------------------------------------------------------------------------------------------------------------------------
371    inline static size_t calculateActualSize( const size_t reportedSize )
372    {
373        // We use DWORDS as our padding, and a long is guaranteed to be 4 bytes,
374        // but an int is not (ANSI defines an int as being the standard word size
375        // for a processor; on a 32-bit machine, that's 4 bytes, but on a 64-bit
376        // machine, it's 8 bytes, which means an int can actually be larger than
377        // a long.)
378
379        return reportedSize + paddingSize * sizeof(long) * 2;
380    }
381
382    // ---------------------------------------------------------------------------------------------------------------------------------
383    inline static size_t calculateReportedSize( const size_t actualSize )
384    {
385        // We use DWORDS as our padding, and a long is guaranteed to be 4 bytes,
386        // but an int is not (ANSI defines an int as being the standard word size
387        // for a processor; on a 32-bit machine, that's 4 bytes, but on a 64-bit
388        // machine, it's 8 bytes, which means an int can actually be larger than a
389        // long.)
390
391        return actualSize - paddingSize * sizeof(long) * 2;
392    }
393
394    // ---------------------------------------------------------------------------------------------------------------------------------
395    inline void *calculateReportedAddress( const void *actualAddress )
396    {
397        // We allow this...
398        if (!actualAddress)
399            return NULL;
400
401        // Just account for the padding
402        return (void *)((char *) actualAddress + sizeof(long) * paddingSize );
403    }
404
405    // ---------------------------------------------------------------------------------------------------------------------------------
406    void wipeWithPattern(
407        sAllocUnit *allocUnit, 
408        unsigned long pattern, 
409        const size_t originalReportedSize = 0 )
410    {
411        // For a serious test run, we use wipes of random a random value. However,
412        // if this causes a crash, we don't want it to crash in a different place
413        // each time, so we specifically DO NOT call srand. If, by chance your
414        // program calls srand(), you may wish to disable that when running with a
415        // random wipe test. This will make any crashes more consistent so they
416        // can be tracked down easier.
417        if( randomWipe )
418        {
419            pattern = ((rand() & 0xff) << 24) | ((rand() & 0xff) << 16) | ((rand() & 0xff) << 8) | (rand() & 0xff);
420        }
421
422        // -DOC- We should wipe with 0's if we're not in debug mode, so we can help
423        // hide bugs if possible when we release the product. So uncomment the
424        // following line for releases.
425        //
426        // Note that the "alwaysWipeAll" should be turned on for this to have
427        // effect, otherwise it won't do much good. But we will leave it this way (as
428        // an option) because this does slow things down.
429
430        //    pattern = 0;
431
432        // This part of the operation is optional
433        if( alwaysWipeAll && allocUnit->reportedSize > originalReportedSize )
434        {
435            // Fill the bulk
436            long  *lptr = (long *) ((char *)allocUnit->reportedAddress + originalReportedSize);
437            size_t length = allocUnit->reportedSize - originalReportedSize;
438            size_t i;
439            for( i = 0; i < (length >> 2); i++, lptr++ )
440            {
441                *lptr = pattern;
442            }
443
444            // Fill the remainder
445            unsigned int shiftCount = 0;
446            char *cptr = (char *) lptr;
447            for( i = 0; i < ( length & 0x3 ); i++, cptr++, shiftCount += 8 )
448            {
449                *cptr =  (char)((( pattern & ( 0xff << shiftCount ) ) >> shiftCount) & 0xff);
450            }
451        }
452
453        // Write in the prefix/postfix bytes
454        long        *pre = (long *)allocUnit->actualAddress;
455        long        *post = (long *)((char *)allocUnit->actualAddress + allocUnit->actualSize - paddingSize * sizeof(long));
456        for (unsigned int i = 0; i < paddingSize; i++, pre++, post++)
457        {
458            *pre = prefixPattern;
459            *post = postfixPattern;
460        }
461    }
462
463    // ---------------------------------------------------------------------------------------------------------------------------------
464    void dumpAllocations(FILE *fp)
465    {
466        fprintf(fp, "Alloc.   Addr       Size       Addr       Size                        BreakOn BreakOn              \r\n");
467        fprintf(fp, "Number Reported   Reported    Actual     Actual     Unused    Method  Dealloc Realloc Allocated by \r\n");
468        fprintf(fp, "------ ---------- ---------- ---------- ---------- ---------- -------- ------- ------- --------------------------------------------------- \r\n");
469
470        for( unsigned int i = 0; i < hashSize; i++ )
471        {
472            sAllocUnit *ptr = hashTable[i];
473            while( ptr )
474            {
475                fprintf(fp, "%06d 0x%08X 0x%08X 0x%08X 0x%08X 0x%08X %-8s    %c       %c    %s(%d) %s\r\n",
476                    ptr->allocationNumber,
477                    (size_t) ptr->reportedAddress, ptr->reportedSize,
478                    (size_t) ptr->actualAddress, ptr->actualSize,
479                    MemoryManager::instance().calcUnused(ptr),
480                    allocationTypes[ptr->allocationType],
481                    ptr->breakOnDealloc ? 'Y':'N',
482                    ptr->breakOnRealloc ? 'Y':'N',
483                    ptr->sourceFile, ptr->sourceLine, ptr->sourceFunc
484                    /*ownerString(ptr->sourceFile, ptr->sourceLine, ptr->sourceFunc)*/);
485                ptr = ptr->next;
486            }
487        }
488    }
489
490    // ---------------------------------------------------------------------------------------------------------------------------------
491    void dumpLeakReport()
492    {
493        // Open the report file
494        FILE *fp = fopen(memoryLeakLogFile, "w+b");
495
496        // If you hit this assert, then the memory report generator is unable to
497        // log information to a file (can't open the file for some reason.)
498        m_assert(fp);
499
500        if( !fp )
501            return;
502
503        // Any leaks?
504
505        // Header
506        char timeString[25];
507        memset( timeString, 0, sizeof(timeString) );
508        time_t t = time(NULL);
509        struct tm *tme = localtime(&t);
510
511        fprintf(fp, " ---------------------------------------------------------------------------------------------------------------------------------- \r\n");
512        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);
513        fprintf(fp, " ---------------------------------------------------------------------------------------------------------------------------------- \r\n");
514        fprintf(fp, "\r\n");
515        fprintf(fp, "\r\n");
516
517        if( stats.totalAllocUnitCount )
518        {
519            fprintf(fp, "%d memory leak%s found:\r\n", 
520                stats.totalAllocUnitCount, 
521                stats.totalAllocUnitCount == 1 ? "":"s" );
522        }
523        else
524        {
525            fprintf(fp, "Congratulations! No memory leaks found!\r\n");
526
527            // We can finally free up our own memory allocations
528            if (reservoirBuffer)
529            {
530                for (unsigned int i = 0; i < reservoirBufferSize; i++)
531                {
532                    free( reservoirBuffer[i] );
533                }
534                free( reservoirBuffer );
535                reservoirBuffer = NULL;
536                reservoirBufferSize = 0;
537                reservoir = NULL;
538            }
539        }
540        fprintf(fp, "\r\n");
541
542        if( stats.totalAllocUnitCount )
543        {
544            dumpAllocations(fp);
545        }
546
547        fclose(fp);
548    }
549
550    /** When chasing a hard-to-pinpoint bug, use this function to make the memory
551        manager exectue a breakpoint when the passed memory location is deallocated.
552    */
553    bool& MemoryManager::breakOnDealloc(void *reportedAddress)
554    {
555    #ifdef _DEBUG
556        // Locate the existing allocation unit
557        sAllocUnit *au = findAllocUnit( reportedAddress );
558
559        // If you hit this assert, you tried to set a breakpoint on deallocation
560        // for an address that doesn't exist. Interrogate the stack frame or the
561        // variable 'au' to see which allocation this is.
562        m_assert(au != NULL);
563
564        return au->breakOnDealloc;
565    #else
566        static bool b;
567        return b;
568    #endif
569    }
570
571    /** When tracking down a hard-to-pinpoint bug, use this function to make the
572        memory manager execute a breakpoint when a specified number of allocations
573        ave been made
574    */
575    void MemoryManager::breakOnAlloc(unsigned int count)
576    {
577    #ifdef _DEBUG
578        breakOnAllocationCount = count;
579    #endif
580    }
581
582    void MemoryManager::setOwner(const char *file, const unsigned int line, const char *func)
583    {
584        // You're probably wondering about this...
585        //
586        // It's important for this memory manager to primarily work with global
587        // new/delete in their original forms (i.e. with no extra parameters.) In
588        // order to do this, we use macros that call this function prior to
589        // operators new & delete. This is fine... usually. Here's what actually
590        // happens when you use this macro to delete an object:
591        //
592        // setOwner( __FILE__, __LINE__, __FUNCTION__ ) --> object::~object() --> delete
593        //
594        // Note that the compiler inserts a call to the object's destructor just
595        // prior to calling our overridden operator delete.
596        //
597        // But what happens when we delete an object whose destructor deletes
598        // another object, whose desctuctor deletes another object? Here's a
599        // diagram (indentation follows stack depth):
600        //
601        // setOwner(...) -> ~obj1()            // original call to delete obj1
602        //     setOwner(...) -> ~obj2()        // obj1's destructor deletes obj2
603        //         setOwner(...) -> ~obj3()    // obj2's destructor deletes obj3
604        //             ...                       // obj3's destructor just does some stuff
605        //         delete                        // back in obj2's destructor, we call delete
606        //     delete                            // back in obj1's destructor, we call delete
607        // delete                                // back to our original call, we call delete
608        //
609        // Because setOwner() just sets up some variables (below) it's important
610        // that each call to setOwner() and successive calls to new/delete
611        // alternate. However, in this case, three calls to setOwner() happen in
612        // succession followed by three calls to delete in succession (with a few
613        // calls to destructors mixed in for fun.) This means that only the final
614        // call to delete (in this chain of events) will have the proper reporting,
615        // and the first two in the chain will not have ANY owner-reporting
616        // information. The deletes will still work fine, we just won't know who
617        // called us.
618        //
619        // "Then build a stack, my friend!" you might think... but it's a very
620        // common thing that people will be working with third-party libraries
621        // (including MFC under Windows) which is not compiled with this memory
622        // manager's macros. In those cases, setOwner() is never called, and
623        // rightfully should not have the proper trace-back information. So if one
624        // of the destructors in the chain ends up being a call to a delete from
625        // a non-mmgr-compiled library, the stack will get confused.
626        //
627        // I've been unable to find a solution to this problem, but at least we can
628        // detect it and report the data before we lose it. That's what this is all
629        // about. It makes it somewhat confusing to read in the logs, but at least
630        // ALL the information is present...
631        //
632        // There's a caveat here... The compiler is not required to call operator
633        // delete if the value being deleted is NULL. In this case, any call to
634        // delete with a NULL will sill call setOwner(), which will make
635        // setOwner() think that there is a destructor chain becuase we setup the
636        // variables, but nothing gets called to clear them. Because of this we
637        // report a "Possible destructor chain".
638        //
639        // Thanks to J. Woznack (from Kodiak Interactive Software Studios --
640        // www.kodiakgames.com) for pointing this out.
641
642        if( sourceLine && alwaysLogAll )
643        {
644            log( "[I] NOTE! Possible destructor chain: previous owner is %s", 
645                ownerString(sourceFile, sourceLine, sourceFunc) );
646        }
647
648        // Okay... save this stuff off so we can keep track of the caller
649        sourceFile = file;
650        sourceLine = line;
651        sourceFunc = func;
652    }
653
654    void resetGlobals()
655    {
656        sourceFile = "??";
657        sourceLine = 0;
658        sourceFunc = "??";
659    }
660
661    /** Allocates a portion of memory.
662    */
663    void* MemoryManager::allocMem(
664        const char *sourceFile, 
665        const unsigned int sourceLine, 
666        const char *sourceFunc, 
667        const unsigned int allocationType, 
668        const size_t reportedSize, 
669        const unsigned processID )
670    {
671        // If we don't have a process ID yet, get one now
672        if( !gProcessID )
673            gProcessID = _getProcessID();
674
675        try
676        {
677            // Increase our allocation count
678            currentAllocationCount++;
679
680            // Log the request
681            if( alwaysLogAll ) 
682                log("[+] %05d %8s of size 0x%08X(%08d) by %s", 
683                    currentAllocationCount, 
684                    allocationTypes[allocationType], 
685                    reportedSize, 
686                    reportedSize, 
687                    ownerString(sourceFile, sourceLine, sourceFunc) );
688
689            // If you hit this assert, you requested a breakpoint on a specific
690            // allocation count
691            m_assert( currentAllocationCount != breakOnAllocationCount );
692
693            // If necessary, grow the reservoir of unused allocation units
694            if( !reservoir )
695            {
696                // Allocate 256 reservoir elements
697                reservoir = (sAllocUnit *) malloc( sizeof(sAllocUnit) * 256 );
698
699                // If you hit this assert, then the memory manager failed to
700                // allocate internal memory for tracking the allocations
701                m_assert( reservoir != NULL );
702
703                // Danger Will Robinson!
704                if( reservoir == NULL ) 
705                    throw "Unable to allocate RAM for internal memory tracking data";
706
707                // Build a linked-list of the elements in our reservoir
708                memset( reservoir, 0, sizeof(sAllocUnit) * 256 );
709                for (unsigned int i = 0; i < 256 - 1; i++)
710                {
711                    reservoir[i].next = &reservoir[i+1];
712                }
713
714                // Add this address to our reservoirBuffer so we can free it later
715                sAllocUnit **temp = (sAllocUnit **)realloc( reservoirBuffer, (reservoirBufferSize + 1) * sizeof(sAllocUnit *) );
716                m_assert( temp );
717                if( temp )
718                {
719                    reservoirBuffer = temp;
720                    reservoirBuffer[reservoirBufferSize++] = reservoir;
721                }
722            }
723
724            // Logical flow says this should never happen...
725            m_assert( reservoir != NULL );
726
727            // Grab a new allocaton unit from the front of the reservoir
728            sAllocUnit    *au = reservoir;
729            reservoir = au->next;
730
731            // Populate it with some real data
732            // HACK the allocator should not need to memset the sAllocUnit
733            // memset( au, 0, sizeof(sAllocUnit) );
734            au->actualSize = calculateActualSize(reportedSize);
735            #ifdef RANDOM_FAILURE
736            double    a = rand();
737            double    b = RAND_MAX / 100.0 * RANDOM_FAILURE;
738            if( a > b )
739            {
740                au->actualAddress = malloc( au->actualSize );
741            }
742            else
743            {
744                log("[F] Random faiure");
745                au->actualAddress = NULL;
746            }
747            #else
748            au->actualAddress     = malloc(au->actualSize);
749            #endif
750            au->reportedSize      = reportedSize;
751            au->reportedAddress   = calculateReportedAddress( au->actualAddress );
752            au->allocationType    = allocationType;
753            au->sourceLine        = sourceLine;
754            au->allocationNumber  = currentAllocationCount;
755            au->processID         = processID;
756
757            if( sourceFile ) 
758                strncpy( au->sourceFile, sourceFileStripper(sourceFile), sizeof(au->sourceFile) - 1 );
759            else
760                strcpy( au->sourceFile, "??" );
761
762            if( sourceFunc ) 
763                strncpy( au->sourceFunc, sourceFunc, sizeof(au->sourceFunc) - 1 );
764            else
765                strcpy( au->sourceFunc, "??" );
766
767            // We don't want to assert with random failures, because we want the application to deal with them.
768
769            #ifndef RANDOM_FAILURE
770            // If you hit this assert, then the requested allocation simply failed
771            // (you're out of memory.) Interrogate the variable 'au' or the stack
772            // frame to see what you were trying to do.
773            m_assert( au->actualAddress != NULL );
774            #endif
775
776            if( au->actualAddress == NULL )
777            {
778                throw "Request for allocation failed. Out of memory.";
779            }
780
781            // If you hit this assert, then this allocation was made from a source
782            // that isn't setup to use this memory tracking software, use the stack
783            // frame to locate the source and include our H file.
784            m_assert( allocationType != m_alloc_unknown );
785
786            if( allocationType == m_alloc_unknown )
787            {
788                log( "[!] Allocation made from outside memory tracker in %s(%d)::%s:", au->sourceFile, au->sourceLine, au->sourceFunc );
789                dumpAllocUnit( au, "  " );
790            }
791
792            // Insert the new allocation into the hash table
793            size_t hashIndex = ((size_t) au->reportedAddress >> 4) & (hashSize - 1);
794            if( hashTable[hashIndex]) 
795            {
796                hashTable[hashIndex]->prev = au;
797            }
798            au->next = hashTable[hashIndex];
799            au->prev = NULL;
800            hashTable[hashIndex] = au;
801
802            // Account for the new allocatin unit in our stats
803            stats.totalReportedMemory += au->reportedSize;
804            stats.totalActualMemory   += au->actualSize;
805            stats.totalAllocUnitCount++;
806
807            if( stats.totalReportedMemory > stats.peakReportedMemory )
808                stats.peakReportedMemory = stats.totalReportedMemory;
809            if( stats.totalActualMemory   > stats.peakActualMemory )
810                stats.peakActualMemory   = stats.totalActualMemory;
811            if( stats.totalAllocUnitCount > stats.peakAllocUnitCount )
812                stats.peakAllocUnitCount = stats.totalAllocUnitCount;
813
814            stats.accumulatedReportedMemory += au->reportedSize;
815            stats.accumulatedActualMemory += au->actualSize;
816            stats.accumulatedAllocUnitCount++;
817
818            // Prepare the allocation unit for use (wipe it with recognizable garbage)
819            wipeWithPattern(au, unusedPattern);
820
821            // calloc() expects the reported memory address range to be filled with 0's
822            memset( au->reportedAddress, 0, au->reportedSize );
823           
824            // Validate every single allocated unit in memory
825            if( alwaysValidateAll )
826                validateAllAllocs();
827
828            // Log the result
829            if( alwaysLogAll )
830                log("[+] ---->             addr 0x%08X", (size_t) au->reportedAddress);
831
832            // Resetting the globals insures that if at some later time, somebody
833            // calls our memory manager from an unknown source (i.e. they didn't
834            // include our H file) then we won't think it was the last allocation.
835            resetGlobals();
836           
837            // Return the (reported) address of the new allocation unit
838            return au->reportedAddress;
839        }
840        catch( const char *err )
841        {
842            // Deal with the errors
843
844            log("[!] %s", err);
845            resetGlobals();
846
847            return NULL;
848        }
849    }
850
851    /** Memory reallocator.
852    */
853    void * MemoryManager::rllocMem(
854        const char *sourceFile, 
855        const unsigned int sourceLine, 
856        const char *sourceFunc, 
857        const unsigned int reallocationType, 
858        const size_t reportedSize, 
859        void *reportedAddress, 
860        const unsigned processID )
861    {
862        // If we don't have a process ID yet, get one now
863        if( !gProcessID )
864            gProcessID = _getProcessID();
865
866        try
867        {
868            // ANSI says: Calling realloc with a NULL should force same operations
869            // as a malloc
870            if( !reportedAddress )
871            {
872                return allocMem(sourceFile, sourceLine, sourceFunc, reallocationType, reportedSize, processID );
873            }
874
875            // Increase our allocation count
876            currentAllocationCount++;
877
878            // If you hit this assert, you requested a breakpoint on a specific
879            // allocation count
880            m_assert( currentAllocationCount != breakOnAllocationCount );
881
882            // Log the request
883            if( alwaysLogAll ) 
884                log("[~] %05d %8s of size 0x%08X(%08d) by %s", 
885                currentAllocationCount, 
886                allocationTypes[reallocationType], 
887                reportedSize, 
888                reportedSize, 
889                ownerString(sourceFile, sourceLine, sourceFunc) );
890
891            // Locate the existing allocation unit
892            sAllocUnit *au = findAllocUnit( reportedAddress );
893
894            // If you hit this assert, you tried to reallocate RAM that wasn't
895            // allocated by this memory manager.
896            m_assert(au != NULL);
897            if( au == NULL )
898                throw "Request to reallocate RAM that was never allocated";
899
900            // If you hit this assert, then the allocation unit that is about to be
901            // reallocated is damaged. But you probably already know that from a
902            // previous assert you should have seen in validateAllocUnit() :)
903            m_assert( validateAlloc( au ) );       
904
905            // If you hit this assert, then this reallocation was made from a source
906            // that isn't setup to use this memory tracking software, use the stack
907            // frame to locate the source and include our H file.
908            m_assert( reallocationType != m_alloc_unknown );
909            if( reallocationType == m_alloc_unknown )
910            {
911                log( "[!] Allocationfrom outside memory tracker in %s(%d)::%s :", sourceFile, sourceLine, sourceFunc );
912                dumpAllocUnit( au, "  " );
913            }
914
915            // If you hit this assert, you were trying to reallocate RAM that was
916            // not allocated in a way that is compatible with realloc. In other
917            // words, you have a allocation/reallocation mismatch.
918            m_assert(
919                au->allocationType == m_alloc_malloc ||
920                au->allocationType == m_alloc_calloc ||
921                au->allocationType == m_alloc_realloc);
922            if( reallocationType == m_alloc_unknown )
923            {
924                log( "[!] Allocation-deallocation mismatch in %s(%d)::%s :", sourceFile, sourceLine, sourceFunc );
925                dumpAllocUnit( au, "  " );
926            }
927
928            // If you hit this assert, then the "break on realloc" flag for this
929            // allocation unit is set (and will continue to be set until you
930            // specifically shut it off. Interrogate the 'au' variable to determine
931            // information about this allocation unit.
932            m_assert( au->breakOnRealloc == false );
933
934            // Keep track of the original size
935            size_t originalReportedSize = au->reportedSize;
936
937            if (alwaysLogAll) log("[~] ---->             from 0x%08X(%08d)", 
938                originalReportedSize, 
939                originalReportedSize);
940
941            // Do the reallocation
942            void   *oldReportedAddress = reportedAddress;
943            size_t newActualSize = calculateActualSize(reportedSize);
944            void   *newActualAddress = NULL;
945
946            #ifdef RANDOM_FAILURE
947
948            double    a = rand();
949            double    b = RAND_MAX / 100.0 * RANDOM_FAILURE;
950            if (a > b)
951            {
952                newActualAddress = realloc(au->actualAddress, newActualSize);
953            }
954            else
955            {
956                log("[F] Random faiure");
957            }
958
959            #else
960
961            newActualAddress = realloc(au->actualAddress, newActualSize);
962
963            #endif
964
965            // We don't want to assert with random failures, because we want the
966            // application to deal with them.
967
968            #ifndef RANDOM_FAILURE
969            // If you hit this assert, then the requested allocation simply failed
970            // (you're out of memory) Interrogate the variable 'au' to see the
971            // original allocation. You can also query 'newActualSize' to see the
972            // amount of memory trying to be allocated. Finally, you can query
973            // 'reportedSize' to see how much memory was requested by the caller.
974            m_assert(newActualAddress);
975            #endif
976
977            if (!newActualAddress) 
978                throw "Request for reallocation failed. Out of memory.";
979
980            // Remove this allocation from our stats (we'll add the new reallocation again later)
981            stats.totalReportedMemory -= au->reportedSize;
982            stats.totalActualMemory   -= au->actualSize;
983
984            // Update the allocation with the new information
985
986            au->actualSize        = newActualSize;
987            au->actualAddress     = newActualAddress;
988            au->reportedSize      = calculateReportedSize(newActualSize);
989            au->reportedAddress   = calculateReportedAddress(newActualAddress);
990            au->allocationType    = reallocationType;
991            au->sourceLine        = sourceLine;
992            au->allocationNumber  = currentAllocationCount;
993            au->processID         = processID;
994            if( sourceFile )
995                strncpy(au->sourceFile, sourceFileStripper(sourceFile), sizeof(au->sourceFile) - 1);
996            else
997                strcpy( au->sourceFile, "??" );
998            if( sourceFunc )
999                strncpy( au->sourceFunc, sourceFunc, sizeof(au->sourceFunc) - 1 );
1000            else
1001                strcpy( au->sourceFunc, "??" );
1002
1003            // The reallocation may cause the address to change, so we should
1004            // relocate our allocation unit within the hash table
1005
1006            size_t hashIndex = (unsigned int) -1;
1007            if( oldReportedAddress != au->reportedAddress )
1008            {
1009                // Remove this allocation unit from the hash table
1010                {
1011                    size_t hashIndex = ((size_t) oldReportedAddress >> 4) & (hashSize - 1);
1012                    if( hashTable[hashIndex] == au )
1013                    {
1014                        hashTable[hashIndex] = hashTable[hashIndex]->next;
1015                    }
1016                    else
1017                    {
1018                        if (au->prev)
1019                            au->prev->next = au->next;
1020                        if (au->next)
1021                            au->next->prev = au->prev;
1022                    }
1023                }
1024
1025                // Re-insert it back into the hash table
1026                hashIndex = ((size_t) au->reportedAddress >> 4) & (hashSize - 1);
1027                if (hashTable[hashIndex]) 
1028                    hashTable[hashIndex]->prev = au;
1029                au->next = hashTable[hashIndex];
1030                au->prev = NULL;
1031                hashTable[hashIndex] = au;
1032            }
1033
1034            // Account for the new allocatin unit in our stats
1035            stats.totalReportedMemory += au->reportedSize;
1036            stats.totalActualMemory   += au->actualSize;
1037            if (stats.totalReportedMemory > stats.peakReportedMemory) 
1038                stats.peakReportedMemory = stats.totalReportedMemory;
1039            if (stats.totalActualMemory   > stats.peakActualMemory)   
1040                stats.peakActualMemory   = stats.totalActualMemory;
1041            size_t deltaReportedSize = reportedSize - originalReportedSize;
1042            if( deltaReportedSize > 0 )
1043            {
1044                stats.accumulatedReportedMemory += deltaReportedSize;
1045                stats.accumulatedActualMemory += deltaReportedSize;
1046            }
1047
1048            // Prepare the allocation unit for use (wipe it with recognizable
1049            // garbage)
1050            wipeWithPattern( au, unusedPattern, originalReportedSize );
1051
1052            // If you hit this assert, then something went wrong, because the
1053            // allocation unit was properly validated PRIOR to the reallocation.
1054            // This should not happen.
1055            m_assert( validateAlloc(au) );
1056
1057            // Validate every single allocated unit in memory
1058            if( alwaysValidateAll ) 
1059                validateAllAllocs();
1060
1061            // Log the result
1062            if (alwaysLogAll) log("[~] ---->             addr 0x%08X", 
1063                (size_t) au->reportedAddress);
1064
1065            // Resetting the globals insures that if at some later time, somebody
1066            // calls our memory manager from an unknown source (i.e. they didn't
1067            // include our H file) then we won't think it was the last allocation.
1068            resetGlobals();
1069
1070            // Return the (reported) address of the new allocation unit
1071            return au->reportedAddress;
1072        }
1073        catch(const char *err)
1074        {
1075            // Deal with the errors
1076            log("[!] %s", err);
1077            resetGlobals();
1078
1079            return NULL;
1080        }
1081    }
1082
1083    // ---------------------------------------------------------------------------------------------------------------------------------
1084    // Deallocate memory and track it
1085    // ---------------------------------------------------------------------------------------------------------------------------------
1086
1087    void MemoryManager::dllocMem(const char *sourceFile, const unsigned int sourceLine, const char *sourceFunc, const unsigned int deallocationType, const void *reportedAddress, const unsigned processID )
1088    {
1089                // early-out for NULL
1090                if (!reportedAddress)
1091                        return;
1092
1093        try
1094        {
1095            // Log the request
1096            if (alwaysLogAll) log("[-] ----- %8s of addr 0x%08X           by %s", 
1097                allocationTypes[deallocationType], 
1098                (size_t) reportedAddress, 
1099                ownerString(sourceFile, sourceLine, sourceFunc) );
1100
1101            // Go get the allocation unit
1102
1103            sAllocUnit *au = findAllocUnit( reportedAddress );
1104
1105            // If you hit this assert, you tried to deallocate RAM that wasn't
1106            // allocated by this memory manager.
1107            m_assert(au != NULL);
1108            if (au == NULL) 
1109                throw "Request to deallocate RAM that was never allocated";
1110
1111            // If you hit this assert, then the allocation unit that is about to be
1112            // deallocated is damaged. But you probably already know that from a
1113            // previous assert you should have seen in validateAllocUnit() :)
1114            m_assert(validateAlloc(au));
1115
1116            // If you hit this assert, then this deallocation was made from a
1117            // source that isn't setup to use this memory tracking software, use
1118            // the stack frame to locate the source and include our H file.
1119            m_assert(deallocationType != m_alloc_unknown);
1120            if( deallocationType == m_alloc_unknown )
1121            {
1122                log( "[!] Allocation-deallocation mismatch in %s(%d)::%s :", sourceFile, sourceLine, sourceFunc );
1123                dumpAllocUnit( au, "  " );
1124            }
1125
1126            // If you hit this assert, you were trying to deallocate RAM that was
1127            // not allocated in a way that is compatible with the deallocation
1128            // method requested. In other words, you have a allocation/deallocation
1129            // mismatch.
1130            m_assert(
1131                (deallocationType == m_alloc_delete       && au->allocationType == m_alloc_new      ) ||
1132                (deallocationType == m_alloc_delete_array && au->allocationType == m_alloc_new_array) ||
1133                (deallocationType == m_alloc_free         && au->allocationType == m_alloc_malloc   ) ||
1134                (deallocationType == m_alloc_free         && au->allocationType == m_alloc_calloc   ) ||
1135                (deallocationType == m_alloc_free         && au->allocationType == m_alloc_realloc  ) ||
1136                (deallocationType == m_alloc_unknown                                                ) );
1137            if( 
1138                !(
1139                (deallocationType == m_alloc_delete       && au->allocationType == m_alloc_new      ) ||
1140                (deallocationType == m_alloc_delete_array && au->allocationType == m_alloc_new_array) ||
1141                (deallocationType == m_alloc_free         && au->allocationType == m_alloc_malloc   ) ||
1142                (deallocationType == m_alloc_free         && au->allocationType == m_alloc_calloc   ) ||
1143                (deallocationType == m_alloc_free         && au->allocationType == m_alloc_realloc  ) ||
1144                (deallocationType == m_alloc_unknown                                                ) ) )
1145            {
1146                log( "[!] Allocation-deallocation mismatch in %s(%d)::%s :", sourceFile, sourceLine, sourceFunc );
1147                dumpAllocUnit( au, "  " );
1148            }
1149
1150            // If you hit this assert, then this deallocation was made from another
1151            // process than the one in which the allocation was made.
1152            // m_assert( au->processID == processID );
1153
1154            // If you hit this assert, then the "break on dealloc" flag for this
1155            // allocation unit is set. Interrogate the 'au'
1156            // variable to determine information about this allocation unit.
1157            m_assert(au->breakOnDealloc == false);
1158
1159            // Wipe the deallocated RAM with a new pattern. This doen't actually do
1160            // us much good in debug mode under WIN32, because Microsoft's memory
1161            // debugging & tracking utilities will wipe it right after we do.
1162            // Oh well.
1163            wipeWithPattern( au, releasedPattern );
1164
1165            // Do the deallocation
1166            free(au->actualAddress);
1167
1168            // Remove this allocation unit from the hash table
1169            size_t hashIndex = ((size_t) au->reportedAddress >> 4) & (hashSize - 1);
1170            if( hashTable[hashIndex] == au )
1171            {
1172                hashTable[hashIndex] = au->next;
1173            }
1174            else
1175            {
1176                if (au->prev)
1177                    au->prev->next = au->next;
1178                if (au->next)
1179                    au->next->prev = au->prev;
1180            }
1181
1182            // Remove this allocation from our stats
1183            stats.totalReportedMemory -= au->reportedSize;
1184            stats.totalActualMemory   -= au->actualSize;
1185            stats.totalAllocUnitCount--;
1186
1187            // Add this allocation unit to the front of our reservoir of unused allocation units
1188            memset( au, 0, sizeof(sAllocUnit) );
1189            au->next = reservoir;
1190            reservoir = au;
1191
1192            // Resetting the globals insures that if at some later time, somebody
1193            // calls our memory manager from an unknown source (i.e. they didn't
1194            // include our H file) then we won't think it was the last allocation.
1195            resetGlobals();
1196
1197            // Validate every single allocated unit in memory
1198            if( alwaysValidateAll )
1199                validateAllAllocs();
1200
1201            // If we're in the midst of  deinitialization time, track any pending memory leaks
1202            if( m_bDeinitTime )
1203                dumpLeakReport();
1204        }
1205        catch(const char *err)
1206        {
1207            // Deal with errors
1208            log("[!] %s", err);
1209            resetGlobals();
1210        }
1211    }
1212
1213    /** By using this function you can be proactive in tracking bugs by being able
1214        to check wether a memory address has been allocated by the memory manager.
1215    */
1216    bool MemoryManager::validateAddr( const void *reportedAddress )
1217    {
1218        // Just see if the address exists in our allocation routines
1219        return findAllocUnit(reportedAddress) != NULL;
1220    }
1221
1222    bool MemoryManager::validateAlloc( const sAllocUnit *allocUnit )
1223    {
1224        // Make sure the padding is untouched
1225        long *pre = (long *)allocUnit->actualAddress;
1226        long *post = (long *)((char *)allocUnit->actualAddress + allocUnit->actualSize - paddingSize * sizeof(long));
1227        bool errorFlag = false;
1228        for( unsigned int i = 0; i < paddingSize; i++, pre++, post++ )
1229        {
1230            if( *pre != (long)prefixPattern )
1231            {
1232                log("[!] A memory allocation unit was corrupt because of an underrun:");
1233                dumpAllocUnit( allocUnit, "  " );
1234                errorFlag = true;
1235            }
1236
1237            // If you hit this assert, then you should know that this allocation
1238            // unit has been damaged. Something (possibly the owner?) has underrun
1239            // the allocation unit (modified a few bytes prior to the start). You
1240            // can interrogate the variable 'allocUnit' to see statistics and
1241            // information about this damaged allocation unit.
1242            m_assert(*pre == (long) prefixPattern);
1243
1244            if (*post != (long) postfixPattern)
1245            {
1246                log("[!] A memory allocation unit was corrupt because of an overrun:");
1247                dumpAllocUnit(allocUnit, "  ");
1248                errorFlag = true;
1249            }
1250
1251            // If you hit this assert, then you should know that this allocation
1252            // unit has been damaged. Something (possibly the owner?) has overrun
1253            // the allocation unit (modified a few bytes after the end). You can
1254            // interrogate the variable 'allocUnit' to see statistics and
1255            // information about this damaged allocation unit.
1256            m_assert(*post == (long) postfixPattern);
1257        }
1258
1259        // Return the error status (we invert it, because a return of 'false' means error)
1260        return !errorFlag;
1261    }
1262
1263    bool MemoryManager::validateAllAllocs()
1264    {
1265        // Just go through each allocation unit in the hash table and count the ones that have errors
1266        unsigned int errors = 0;
1267        unsigned int allocCount = 0;
1268
1269        for( unsigned int i = 0; i < hashSize; i++ )
1270        {
1271            sAllocUnit *ptr = hashTable[i];
1272            while(ptr)
1273            {
1274                allocCount++;
1275                if (!validateAlloc(ptr)) 
1276                    errors++;
1277                ptr = ptr->next;
1278            }
1279        }
1280
1281        // Test for hash-table correctness
1282        if( allocCount != stats.totalAllocUnitCount )
1283        {
1284            log("[!] Memory tracking hash table corrupt!");
1285            errors++;
1286        }
1287
1288        // If you hit this assert, then the internal memory (hash table) used by
1289        // this memory tracking software is damaged! The best way to track this
1290        // down is to use the alwaysLogAll flag in conjunction with STRESS_TEST
1291        // macro to narrow in on the offending code. After running the application
1292        // with these settings (and hitting this assert again), interrogate the
1293        // memory.log file to find the previous successful operation. The
1294        // corruption will have occurred between that point and this assertion.
1295        m_assert( allocCount == stats.totalAllocUnitCount );
1296
1297        // If you hit this assert, then you've probably already been notified that
1298        // there was a problem with a allocation unit in a prior call to
1299        // validateAllocUnit(), but this assert is here just to make sure you know
1300        // about it. :)
1301        m_assert( errors == 0 );
1302
1303        // Log any errors
1304        if (errors) 
1305            log("[!] While validating all allocation units, %d allocation unit(s) were found to have problems", 
1306            errors );
1307
1308        // Return the error status
1309        return errors != 0;
1310    }
1311
1312    /** Determines how much RAM is unused.
1313    */
1314    unsigned int MemoryManager::calcUnused( const sAllocUnit *allocUnit )
1315    {
1316        const unsigned long *ptr = (const unsigned long *)allocUnit->reportedAddress;
1317        unsigned int count = 0;
1318
1319        for( unsigned int i = 0; i < allocUnit->reportedSize; i += sizeof(long), ptr++ )
1320        {
1321            if (*ptr == unusedPattern) count += sizeof(long);
1322        }
1323
1324        return count;
1325    }
1326
1327    unsigned int MemoryManager::calcAllUnused()
1328    {
1329        // Just go through each allocation unit in the hash table and count the
1330        // unused RAM
1331        unsigned int total = 0;
1332        for( unsigned int i = 0; i < hashSize; i++ )
1333        {
1334            sAllocUnit *ptr = hashTable[i];
1335            while(ptr)
1336            {
1337                total += calcUnused(ptr);
1338                ptr = ptr->next;
1339            }
1340        }
1341
1342        return total;
1343    }
1344
1345    void MemoryManager::dumpAllocUnit(const sAllocUnit *allocUnit, const char *prefix)
1346    {
1347        log("[I] %sAddress (reported): %010p",       prefix, allocUnit->reportedAddress);
1348        log("[I] %sAddress (actual)  : %010p",       prefix, allocUnit->actualAddress);
1349        log("[I] %sSize (reported)   : 0x%08X (%s)", prefix, allocUnit->reportedSize, 
1350                                                    memorySizeString(allocUnit->reportedSize));
1351        log("[I] %sSize (actual)     : 0x%08X (%s)", prefix, allocUnit->actualSize, 
1352                                                    memorySizeString(allocUnit->actualSize));
1353        log("[I] %sOwner             : %s(%d)::%s",  prefix, allocUnit->sourceFile, allocUnit->sourceLine, allocUnit->sourceFunc);
1354        log("[I] %sAllocation type   : %s",          prefix, allocationTypes[allocUnit->allocationType]);
1355        log("[I] %sAllocation number : %d",          prefix, allocUnit->allocationNumber);
1356    }
1357
1358    void MemoryManager::dumpMemReport(const char *filename, const bool overwrite)
1359    {
1360        // Open the report file
1361        FILE    *fp = NULL;
1362       
1363        if (overwrite)   
1364            fp = fopen(filename, "w+b");
1365        else       
1366            fp = fopen(filename, "ab");
1367
1368        // If you hit this assert, then the memory report generator is unable to
1369        // log information to a file (can't open the file for some reason.)
1370        m_assert(fp);
1371        if (!fp) 
1372            return;
1373
1374        // Header
1375        char    timeString[25];
1376        memset(timeString, 0, sizeof(timeString));
1377        time_t  t = time(NULL);
1378        struct  tm *tme = localtime(&t);
1379
1380        fprintf(fp, " ---------------------------------------------------------------------------------------------------------------------------------- \r\n");
1381        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);
1382        fprintf(fp, " ---------------------------------------------------------------------------------------------------------------------------------- \r\n");
1383        fprintf(fp, "\r\n");
1384        fprintf(fp, "\r\n");
1385
1386        // Report summary
1387        fprintf(fp, " ---------------------------------------------------------------------------------------------------------------------------------- \r\n");
1388        fprintf(fp, "|                                                           T O T A L S                                                            |\r\n");
1389        fprintf(fp, " ---------------------------------------------------------------------------------------------------------------------------------- \r\n");
1390        fprintf(fp, "              Allocation unit count: %10s\r\n", insertCommas(stats.totalAllocUnitCount));
1391        fprintf(fp, "            Reported to application: %s\r\n", memorySizeString(stats.totalReportedMemory));
1392        fprintf(fp, "         Actual total memory in use: %s\r\n", memorySizeString(stats.totalActualMemory));
1393        fprintf(fp, "           Memory tracking overhead: %s\r\n", memorySizeString(stats.totalActualMemory - stats.totalReportedMemory));
1394        fprintf(fp, "\r\n");
1395
1396        fprintf(fp, " ---------------------------------------------------------------------------------------------------------------------------------- \r\n");
1397        fprintf(fp, "|                                                            P E A K S                                                             |\r\n");
1398        fprintf(fp, " ---------------------------------------------------------------------------------------------------------------------------------- \r\n");
1399        fprintf(fp, "              Allocation unit count: %10s\r\n", insertCommas(stats.peakAllocUnitCount));
1400        fprintf(fp, "            Reported to application: %s\r\n", memorySizeString(stats.peakReportedMemory));
1401        fprintf(fp, "                             Actual: %s\r\n", memorySizeString(stats.peakActualMemory));
1402        fprintf(fp, "           Memory tracking overhead: %s\r\n", memorySizeString(stats.peakActualMemory - stats.peakReportedMemory));
1403        fprintf(fp, "\r\n");
1404
1405        fprintf(fp, " ---------------------------------------------------------------------------------------------------------------------------------- \r\n");
1406        fprintf(fp, "|                                                      A C C U M U L A T E D                                                       |\r\n");
1407        fprintf(fp, " ---------------------------------------------------------------------------------------------------------------------------------- \r\n");
1408        fprintf(fp, "              Allocation unit count: %s\r\n", memorySizeString(stats.accumulatedAllocUnitCount));
1409        fprintf(fp, "            Reported to application: %s\r\n", memorySizeString(stats.accumulatedReportedMemory));
1410        fprintf(fp, "                             Actual: %s\r\n", memorySizeString(stats.accumulatedActualMemory));
1411        fprintf(fp, "\r\n");
1412
1413        fprintf(fp, " ---------------------------------------------------------------------------------------------------------------------------------- \r\n");
1414        fprintf(fp, "|                                                           U N U S E D                                                            |\r\n");
1415        fprintf(fp, " ---------------------------------------------------------------------------------------------------------------------------------- \r\n");
1416        fprintf(fp, "    Memory allocated but not in use: %s\r\n", memorySizeString(calcAllUnused()));
1417        fprintf(fp, "\r\n");
1418
1419        dumpAllocations(fp);
1420
1421        fclose(fp);
1422    }
1423
1424    sMStats    MemoryManager::getMemStats()
1425    {
1426        return stats;
1427    }
1428
1429    // ---------------------------------------------------------------------------------------------------------------------------------
1430    // Global new/new[]
1431    //
1432    // These are the standard new/new[] operators. They are merely interface functions that operate like normal new/new[], but use our
1433    // memory tracking routines.
1434    // ---------------------------------------------------------------------------------------------------------------------------------
1435    void *MemoryManager::op_new_sc( size_t reportedSize, unsigned processID )
1436    {
1437        // Save these off...
1438        const    char        *file = sourceFile;
1439        const    unsigned int line = sourceLine;
1440        const    char        *func = sourceFunc;
1441
1442        // ANSI says: allocation requests of 0 bytes will still return a valid value
1443        if (reportedSize == 0) reportedSize = 1;
1444
1445        // ANSI says: loop continuously because the error handler could possibly free up some memory
1446        for(;;)
1447        {
1448            // Try the allocation
1449            void *ptr = allocMem(file, line, func, m_alloc_new, reportedSize, processID );
1450            if( ptr )
1451            {
1452                return ptr;
1453            }
1454
1455            // There isn't a way to determine the new handler, except through setting it. So we'll just set it to NULL, then
1456            // set it back again.
1457            std::new_handler nh = std::set_new_handler(0);
1458            std::set_new_handler(nh);
1459
1460            // If there is an error handler, call it
1461            if (nh)
1462            {
1463                (*nh)();
1464            }
1465
1466            // Otherwise, throw the exception
1467            else
1468            {
1469                throw std::bad_alloc();
1470            }
1471        }
1472    }
1473
1474    // ---------------------------------------------------------------------------------------------------------------------------------
1475    void *MemoryManager::op_new_vc( size_t reportedSize, unsigned processID )
1476    {
1477        // Save these off...
1478        const    char        *file = sourceFile;
1479        const    unsigned int line = sourceLine;
1480        const    char        *func = sourceFunc;
1481
1482        // The ANSI standard says that allocation requests of 0 bytes will still return a valid value
1483        if (reportedSize == 0) reportedSize = 1;
1484
1485        // ANSI says: loop continuously because the error handler could possibly free up some memory
1486        for(;;)
1487        {
1488            // Try the allocation
1489            void    *ptr = allocMem(file, line, func, m_alloc_new_array, reportedSize, processID );
1490            if( ptr )
1491            {
1492                return ptr;
1493            }
1494
1495            // There isn't a way to determine the new handler, except through setting it. So we'll just set it to NULL, then
1496            // set it back again.
1497            std::new_handler    nh = std::set_new_handler(0);
1498            std::set_new_handler(nh);
1499
1500            // If there is an error handler, call it
1501            if (nh)
1502            {
1503                (*nh)();
1504            }
1505
1506            // Otherwise, throw the exception
1507            else
1508            {
1509                throw std::bad_alloc();
1510            }
1511        }
1512    }
1513
1514    // ---------------------------------------------------------------------------------------------------------------------------------
1515    // Other global new/new[]
1516    //
1517    // These are the standard new/new[] operators as used by Microsoft's memory tracker. We don't want them interfering with our memory
1518    // tracking efforts. Like the previous versions, these are merely interface functions that operate like normal new/new[], but use
1519    // our memory tracking routines.
1520    // ---------------------------------------------------------------------------------------------------------------------------------
1521    void *MemoryManager::op_new_sc( size_t reportedSize, const char *sourceFile, int sourceLine, unsigned processID )
1522    {
1523        // The ANSI standard says that allocation requests of 0 bytes will still
1524        // return a valid value
1525        if( reportedSize == 0 )
1526            reportedSize = 1;
1527
1528        // ANSI says: loop continuously because the error handler could possibly
1529        // free up some memory
1530        for(;;)
1531        {
1532            // Try the allocation
1533
1534            void    *ptr = allocMem(sourceFile, sourceLine, "??", m_alloc_new, reportedSize, processID );
1535            if (ptr)
1536            {
1537                return ptr;
1538            }
1539
1540            // There isn't a way to determine the new handler, except through setting it. So we'll just set it to NULL, then
1541            // set it back again.
1542
1543            std::new_handler    nh = std::set_new_handler(0);
1544            std::set_new_handler(nh);
1545
1546            // If there is an error handler, call it
1547
1548            if (nh)
1549            {
1550                (*nh)();
1551            }
1552
1553            // Otherwise, throw the exception
1554
1555            else
1556            {
1557                throw std::bad_alloc();
1558            }
1559        }
1560    }
1561
1562    // ---------------------------------------------------------------------------------------------------------------------------------
1563    void *MemoryManager::op_new_vc(size_t reportedSize, const char *sourceFile, int sourceLine, unsigned processID )
1564    {
1565        // ANSI says : allocation requests of 0 bytes will still return a valid
1566        // value
1567        if( reportedSize == 0 )
1568            reportedSize = 1;
1569
1570        // ANSI says: loop continuously because the error handler could possibly
1571        // free up some memory
1572
1573        for(;;)
1574        {
1575            // Try the allocation
1576            void *ptr = allocMem(
1577                sourceFile, 
1578                sourceLine, 
1579                "??", 
1580                m_alloc_new_array, 
1581                reportedSize, 
1582                processID );
1583            if( ptr )
1584            {
1585                return ptr;
1586            }
1587
1588            // There isn't a way to determine the new handler, except through
1589            // setting it. So we'll just set it to NULL, then set it back again.
1590            std::new_handler nh = std::set_new_handler(0);
1591            std::set_new_handler(nh);
1592
1593            // If there is an error handler, call it
1594            if( nh )
1595            {
1596                (*nh)();
1597            }
1598
1599            // Otherwise, throw the exception
1600            else
1601            {
1602                throw std::bad_alloc();
1603            }
1604        }
1605    }
1606
1607    // ---------------------------------------------------------------------------------------------------------------------------------
1608    // Global delete/delete[]
1609    //
1610    // These are the standard delete/delete[] operators. They are merely interface functions that operate like normal delete/delete[],
1611    // but use our memory tracking routines.
1612    // ---------------------------------------------------------------------------------------------------------------------------------
1613    void MemoryManager::op_del_sc(void *reportedAddress, unsigned processID )
1614    {
1615        // ANSI says: delete & delete[] allow NULL pointers (they do nothing)
1616        if( reportedAddress )
1617            dllocMem(sourceFile, sourceLine, sourceFunc, m_alloc_delete, reportedAddress, processID );
1618        else if( alwaysLogAll )
1619            log("[-] ----- %8s of NULL                      by %s", 
1620            allocationTypes[m_alloc_delete], 
1621            ownerString(sourceFile, sourceLine, sourceFunc) );
1622
1623        // Resetting the globals insures that if at some later time, somebody calls
1624        // our memory manager from an unknown source (i.e. they didn't include our
1625        // H file) then we won't think it was the last allocation.
1626        resetGlobals();
1627    }
1628
1629    // ---------------------------------------------------------------------------------------------------------------------------------
1630    void MemoryManager::op_del_vc(void *reportedAddress, unsigned processID )
1631    {
1632        // ANSI says: delete & delete[] allow NULL pointers (they do nothing)
1633        if (reportedAddress) 
1634            dllocMem(
1635                sourceFile, 
1636                sourceLine, 
1637                sourceFunc, 
1638                m_alloc_delete_array, 
1639                reportedAddress, 
1640                processID );
1641        else if( alwaysLogAll )
1642            log("[-] ----- %8s of NULL                      by %s", allocationTypes[m_alloc_delete_array], ownerString(sourceFile, sourceLine, sourceFunc));
1643
1644        // Resetting the globals insures that if at some later time, somebody calls
1645        // our memory manager from an unknown source (i.e. they didn't include our
1646        // H file) then we won't think it was the last allocation.
1647        resetGlobals();
1648    }
1649
1650    MemoryManager::MemoryManager()
1651        : m_uProcessIDs( 0 ), m_bDeinitTime( false )
1652    {
1653        doCleanupLogOnFirstRun();
1654    }
1655
1656    MemoryManager::~MemoryManager()
1657    {
1658        m_bDeinitTime = true;
1659        dumpLeakReport();   
1660    }
1661
1662    unsigned MemoryManager::_getProcessID()
1663    {
1664        return ++m_uProcessIDs;
1665    }
1666 
1667#else
1668
1669    //-----------------------------------------------------------------------------
1670    MemoryManager::MemoryManager()
1671    {   
1672    }
1673
1674    //-----------------------------------------------------------------------------
1675    MemoryManager::~MemoryManager()
1676    {               
1677    }
1678
1679    //-----------------------------------------------------------------------------
1680    void * MemoryManager::allocMem( const char *szFile, size_t uLine, 
1681            size_t count ) throw()
1682    {
1683        void *ptr = malloc( count );
1684        return ptr;
1685    }       
1686
1687    //-----------------------------------------------------------------------------
1688    void * MemoryManager::rllocMem( 
1689        const char *szFile, size_t uLine, void *ptr , size_t count ) throw()
1690    {
1691        void *nptr = realloc( ptr, count );
1692        return nptr;
1693    }
1694
1695    //-----------------------------------------------------------------------------
1696    void * MemoryManager::cllocMem( 
1697        const char *szFile, size_t uLine, size_t num, size_t size ) throw()
1698    {
1699        void *ptr = malloc( num * size );
1700
1701        if( ptr )
1702        {
1703            memset( ptr , 0, num * size );
1704        }
1705        return ptr;
1706    }
1707
1708    //-----------------------------------------------------------------------------
1709    void MemoryManager::dllocMem( const char *szFile, size_t uLine, 
1710            void *ptr) throw()
1711    {
1712        free( ptr );
1713    }
1714
1715#endif // OGRE_DEBUG_MEMORY_MANAGER
1716
1717}
1718
Note: See TracBrowser for help on using the repository browser.