source: OGRE/trunk/ogrenew/OgreMain/src/OgreMemoryManager.cpp @ 690

Revision 690, 72.4 KB checked in by mattausch, 18 years ago (diff)

added ogre 1.07 main

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