Memory tracker

Started by
25 comments, last by RDragon1 18 years ago
Hi, I would like to write a series of functions which I can use in my applications which can track current memory usage by the application (so I can identify memory leaks) and I would also like to be able to identify and catch exceptions. If this is possible, would someone please point me in the right direction. I realise it's not a menial task, but right now I dont even know where to start looking. This kind of cuntionality would be invaluble. Many thanks, TheBob
Advertisement
You can overload operator new, operator new[], operator delete and operator delete[] or use the readymade memory tracker mmgr by Paul Nettle.
The following is a simplified version of the code I use to perform memory tracking...

//-----------------------------------------------------------------------------// File            : MemoryLeakTracker.h// Author          : Shaun Dwane White// Creation Date   : 03-04-06// Revision Date   : 03-04-06// Version         : 1.0//// Notes           : Header file for MemoryLeakTracker.//-----------------------------------------------------------------------------//-----------------------------------------------------------------------------// Header file inclusion guard.//-----------------------------------------------------------------------------#ifndef MEMORY_LEAK_TRACKER__H#define MEMORY_LEAK_TRACKER__H//-----------------------------------------------------------------------------// Standard includes for this header file to work.//-----------------------------------------------------------------------------#include <windows.h>#include <crtdbg.h>#include <stdlib.h>#include <stdio.h>#include <tchar.h>//-----------------------------------------------------------------------------// Microsoft specific compiler symbol, that is defined during DEBUG builds and// undefined during RELEASE builds. This flag is used to ensure that the// memory tracking macros are only defined in DEBUG builds.//-----------------------------------------------------------------------------#ifdef _DEBUG//-----------------------------------------------------------------------------// DEBUG Macros://-----------------------------------------------------------------------------//-----------------------------------------------------------------------------// SNAP_HEAP:// Takes a snap shot of the current debug heap. MUST be used in conjuntion// with a following TEST_HEAP macro in the SAME code statement block.//// TEST_HEAP:// Takes another snap shot of the debug heap and compares it with the previous// snap shot obtained by the previous SNAP_HEAP macro. If there is a// difference, then a message box will appear, warning you that a leak has// occurred. The message box will also display the file and line number where// the detection took place, as well as giving you the option to continue or// abort the program.//// Notes:// Simply detecting a leak is sufficient for my purposes, but you could alter// the code easily to determine just how much bytes were leaked and in what// heap etc. You can even obtain a dump of the heap statistics if you like...// Just check out the _CrtMem... functions defined in "crtdbg.h" and explained// in MSDN.//// Also note that the _CrtMem... functions are DEBUG only functions.//-----------------------------------------------------------------------------#define	SNAP_HEAP                   *{                                   *    _CrtMemState oldHeap;           *                                    *    _CrtMemCheckpoint (&oldHeap);   *    {#define	TEST_HEAP                                                                                                               *    }                                                                                                                           *    _CrtMemState newHeap, heapDifference;                                                                                       *    _TCHAR msgBoxText [MAX_PATH + 1];                                                                                           *                                                                                                                                *    _CrtMemCheckpoint (&newHeap);                                                                                               *    if (_CrtMemDifference (&heapDifference, &oldHeap, &newHeap))                                                                *    {                                                                                                                           *        _sntprintf                                                                                                              *        (                                                                                                                       *            msgBoxText, MAX_PATH, _TEXT ("Memory leak detected!\n\nIn file:\n%s\n\nAt line:\n%d\n\nExit process?"),             *            _TEXT (__FILE__), __LINE__                                                                                          *        );                                                                                                                      *        msgBoxText[MAX_PATH] = 0;                                                                                               *        if (MessageBox (0, msgBoxText, TEXT ("Memory Leak!!!"), MB_YESNO | MB_ICONWARNING) != IDNO) ExitProcess (EXIT_FAILURE); *    }                                                                                                                           *}//-----------------------------------------------------------------------------// SNAP_HEAP_STATE (heap):// Takes a snap shot of the debug heap and stores the result in "heap". This// is a more flexible version of the SNAP_HEAP macro, because if "heap" is// defined globally, then you don't necessarily have to perform a test in the// same code block.//// TEST_HEAP_STATE (heap):// Performs exactly like the TEST_HEAP macro, except it now tests the "heap"// variable.//// Notes:// These macros are more flexible than the previous pair, as it can detect// memory leaks that occur across function calls. Useful for general// programming issues as well as detecting leaks in DLLs (provided you perform// the test in DllMain's DLL_PROCESS_ATTACH and DLL_PROCESS_DETACH cases).//// The variable "heap" MUST be of the type: _CrtMemState//-----------------------------------------------------------------------------#define SNAP_HEAP_STATE(heap) _CrtMemCheckpoint (static_cast<_CrtMemState *> (&heap));#define TEST_HEAP_STATE(oldHeap)                                                                                                    *{                                                                                                                                   *    _TCHAR ___msgBoxText [MAX_PATH + 1];                                                                                            *    _CrtMemState ___newHeap, ___heapDifference;                                                                                     *                                                                                                                                    *    _CrtMemCheckpoint (&___newHeap);                                                                                                *    if (_CrtMemDifference (&___heapDifference, static_cast<const _CrtMemState *> (&oldHeap), &___newHeap))                          *    {                                                                                                                               *        _sntprintf                                                                                                                  *        (                                                                                                                           *            ___msgBoxText, MAX_PATH, _TEXT ("Memory leak detected!\n\nIn file:\n%s\n\nAt line:\n%d\n\nExit process?"),              *            _TEXT (__FILE__), __LINE__                                                                                              *        );                                                                                                                          *        ___msgBoxText[MAX_PATH] = 0;                                                                                                *        if (MessageBox (0, ___msgBoxText, TEXT ("Memory Leak!!!"), MB_YESNO | MB_ICONWARNING) != IDNO) ExitProcess (EXIT_FAILURE);  *    }                                                                                                                               *}//-----------------------------------------------------------------------------// RELEASE Macros:// Ensures that all memory tracking macros are omitted in RELEASE builds.//-----------------------------------------------------------------------------#else#define SNAP_HEAP#define TEST_HEAP#define SNAP_HEAP_STATE(heap)       0;#define TEST_HEAP_STATE(oldHeap)    0;#endif // _DEBUG#endif // MEMORY_LEAK_TRACKER__H


IMPORTANT NOTE!!! If you copy and paste the above code, you must turn all ending '*' characters into backslash characters '\'. For some reason
(my English keyboard I think), '\' characters don't print very well here.

Here is a source code example of how one would this code:

#include "MemoryLeakTracker.h"#ifdef _DEBUG_CrtMemState debugHeap;#endif // _DEBUGvoid MemoryLeakFunction (PBYTE memoryLeak){    if (memoryLeak) memoryLeak = new BYTE;}int main (){    PBYTE memoryLeakOne;    PBYTE memoryLeakTwo;//----------------------------------------------------------------------------------------------------------------------------------------// SNAP_HEAP, TEST_HEAP example://----------------------------------------------------------------------------------------------------------------------------------------    SNAP_HEAP    {        memoryLeakOne = new BYTE;    }    TEST_HEAP//----------------------------------------------------------------------------------------------------------------------------------------// SNAP_HEAP_STATE, TEST_HEAP_STATE example://----------------------------------------------------------------------------------------------------------------------------------------    SNAP_HEAP_STATE (debugHeap)    MemoryLeakFunction (memoryLeakTwo);    TEST_HEAP_STATE (debugHeap)    return EXIT_SUCCESS;}


And here is a simplified version of the code I use for handling exceptions:

//-----------------------------------------------------------------------------// File            : SimpleExceptionHandler.h// Author          : Shaun Dwane White// Creation Date   : 03-04-06// Revision Date   : 03-04-06// Version         : 1.0//// Notes           : Header file for SimpleExceptionHandler.//-----------------------------------------------------------------------------//-----------------------------------------------------------------------------// Header file inclusion guard.//-----------------------------------------------------------------------------#ifndef SIMPLE_EXCEPTION_HANDLER__H#define SIMPLE_EXCEPTION_HANDLER__H//-----------------------------------------------------------------------------// Standard include for this header file to work.//-----------------------------------------------------------------------------#include <windows.h>//-----------------------------------------------------------------------------// Exception structure declarations://-----------------------------------------------------------------------------//-----------------------------------------------------------------------------// General EXCEPTION structure declaration://// This structure is thrown when a general exception is raised and is used as// a BASE structure for all other types of exceptions.//-----------------------------------------------------------------------------struct EXCEPTION{    LPCTSTR ITS_MSG;    // The exception's message string.    LPCTSTR ITS_FILE;   // The name of the file in which the exception is thrown.    DWORD   itsLine;    // The line number where the exception was thrown.    PVOID   itsParam;   // Extra parameter for future expansion.    EXCEPTION (LPCTSTR, LPCTSTR, DWORD, PVOID = 0);};//-----------------------------------------------------------------------------// MEMORY_EXCEPTION structure declaration://// This structure is specifically thrown when memory can no longer be allocated.//-----------------------------------------------------------------------------struct MEMORY_EXCEPTION : public EXCEPTION{    MEMORY_EXCEPTION (LPCTSTR, LPCTSTR, DWORD, PVOID = 0);};//-----------------------------------------------------------------------------// You could introduce further exception structures here in a similar way, like // DRIVE_EXCEPTION for disk drive problems, or GRAPHICS_EXCEPTION for video// card problems etc.//-----------------------------------------------------------------------------//-----------------------------------------------------------------------------// Exception structure inlined implementations://// These structures are pretty simple, so we may as well inline their// implementations here in this header file. They'll perform a bit quicker too!//-----------------------------------------------------------------------------//-----------------------------------------------------------------------------//	General EXCEPTION structure implementation://-----------------------------------------------------------------------------inline EXCEPTION::EXCEPTION (LPCTSTR MSG, LPCTSTR FILE, DWORD line, PVOID param):    ITS_MSG     (MSG),    ITS_FILE    (FILE),    itsLine     (line),    itsParam    (param){}//-----------------------------------------------------------------------------//	MEMORY_EXCEPTION structure implementation://-----------------------------------------------------------------------------inline MEMORY_EXCEPTION::MEMORY_EXCEPTION (LPCTSTR MSG, LPCTSTR FILE, DWORD line, PVOID param):    EXCEPTION (MSG, FILE, line, param){}#endif // SIMPLE_EXCEPTION_HANDLER__H


And finally, here is a source code example of how one would use this simple exception handler:

#include "ExceptionHandler.h"#include <stdio.h>#include <tchar.h>void PrintException (const EXCEPTION &EXCEPTION){    _tprintf ("ITS_MSG\t\t%s\n",    EXCEPTION.ITS_MSG);    _tprintf ("ITS_FILE\t%s\n",     EXCEPTION.ITS_FILE);    _tprintf ("itsLine\t\t%u\n",    EXCEPTION.itsLine);    _tprintf ("itsParam\t%s\n\n",   EXCEPTION.itsParam);}int main (){//-----------------------------------------------------------------------------// Throw and catch a general exception://// Note:// The pre-defined symbols __FILE__ is turned into a string which contains the// filename of the current file by the compiler during compilation.//// The pre-defined symbols __LINE__ is tured into the current code line number// by the compiler during compilation.//-----------------------------------------------------------------------------    try    {        throw EXCEPTION (_TEXT ("General Exception Thrown!"), __FILE__, __LINE__);    }    catch (EXCEPTION &EXCEPTION)    {        PrintException (EXCEPTION);    }    catch (...)    {        puts ("An unknown exception has been caught!");    }//-----------------------------------------------------------------------------// Throw and catch a memory exception://// Note:// If you specifically wanted to catch memory exception, you must place them// ABOVE the code where general or undefined exceptions would be caught.//-----------------------------------------------------------------------------    try    {        throw MEMORY_EXCEPTION (_TEXT ("Out Of Memory!"), __FILE__, __LINE__);    }    catch (MEMORY_EXCEPTION &MEMORY_EXCEPTION)    {        PrintException (MEMORY_EXCEPTION);    }    catch (EXCEPTION &EXCEPTION)    {        PrintException (EXCEPTION);    }    catch (...)    {        puts ("An unknown exception has been caught!");    }    return 0;}


Please feel free to copy and paste the code if it suits your needs.

I hope this helps!

[Edited by - TerrorFLOP on April 3, 2006 12:53:56 AM]
If you're using Visual C++, you could also use their memory trakcer with

#include <crtdbg.h> // in the stdafx.h
_CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF); // in the main entry point of your program

It will prints in the debug output of visual a list of all the memory links, with an allocID. With this allocID, you can then re-execute the program, and break when it is reached, using this line :

_CrtSetBreakAlloc(5031); // where 5031 is the allocID reported by the previous execution
I feel like a peasant amoung gods. Thanks guys. Most helpfull.
Quote:Original post by TerrorFLOP
...


You mix C and C++ code. The cast static_cast is not available in C, the headers stdlib.h and stdio.h aren't available in C++. There is some other stuff which also is only in one of the languages. Your code mostly looks like C with some stuff borrowed from C++, but this isn't standard. If it's supposed to be C++ code then you should not use the C functions like _sntprintf, _tprintf etc. Instead you should use the C++ standard library (You would need streams). Also you know in C++ (not sure about C) you are not allowed (well it might just result in undefined behavior, can't remember, but it's not guareented to act like a normal variable) to start your variable names with __ (two underscores), since those are reserved for the compiler..
stdio.h, stdlib.h, etc., as well as the *printf family of functions are all perfectly legal C++.
they are legal as C is a subset of C++, but... they are deprecated and their use is discouraged for pure C++
Steven ToveySPUify | Twitter
I've been mixing C and C++ for years.

It's ALL perfectly legal under MS Visual C++ 6.0.

As my old grandma used to say...

'If you can, have the best of both worlds...'

I tend to use TRIPLE underscoring in my #define symbols.

This is my OWN programming style (again perfectly legal), which ensures I don't have a symbol clash with other #defines. And since some symbols have DOUBLE underscoring... I thought TRIPLE underscoring would be a wise precaution, considering that #define symbols cannot be namespaced.

Again, my own style... That suits me just fine thx!

BTW... Double underscoring or indeed single underscoring... Or having varibles and symbols that start with TEN underscores is ALL prefectly legal (C/C++ are very flexible), so long as you don't have a clash with an already defined variable or symbol.
Quote:Original post by Omaha
stdio.h, stdlib.h, etc., as well as the *printf family of functions are all perfectly legal C++.


stdio.h, stdlib.h, etc are NOT part of C++. You're thinking of cstdio, cstdlib, etc. Which are the C++ headers that support the C89 standard library.

This topic is closed to new replies.

Advertisement