Click here to Skip to main content
15,885,365 members
Articles / Programming Languages / C++

Check for and free memory leaks

Rate me:
Please Sign up or sign in to vote.
4.67/5 (9 votes)
24 Jul 200310 min read 152.5K   3.1K   53  
An article on detection and freeing of memory leaks
// The MemLeakCheck source code is by Robert Walker
// support@tunesmithy.co.uk

// To check for updates visit
// http://tunesmithy.co.uk/memleakcheck/index.htm

#ifndef MEMLEAKCHECK_H_INCLUDED
#define MEMLEAKCHECK_H_INCLUDED

// Include this header after the stdio, stdlib malloc etc ones in your program.
// If you use the MSVC memory leak check normally and are temporarily overriding
// it, include this after the #include <crtdbg.h> 

// Uncomment these as needed - for info about them see below or the web site.

#define cl_mem_leak_check 
#define cl_mem_leak_diagnostics

#define cl_qsort_pointers // sorts the diagnostic reports by file, line and size
                             // - may be slow if you have many pointers 

// #define LINK_MEMLEAK_CHECK_AS_DLL
// #define BUILD_MEMLEAK_CHECK_AS_DLL 
#define MLC_MAKE_WINDOWS_THREAD_SAFE
// #define MLC_USE_THREAD_LOCAL_VARIABLES
// #define code_uses_redefined_malloc

#define MLC_WARN_ON_ATTEMPT_TO_FREE_WILD_POINTER 
 /* In diagnostic mode, this gives a warning if you free a pointer which you never
  * allocated, or allocated and have already freed
  */


#ifdef cl_mem_leak_diagnostics
 #define cl_mem_leak_check 
#endif


#ifdef MLC_USE_THREAD_LOCAL_VARIABLES
 #undef MLC_MAKE_WINDOWS_THREAD_SAFE 
 // not needed as each thread has its own version of the pointers array 
#endif


#ifdef BUILD_MEMLEAK_CHECK_AS_DLL
 #undef LINK_MEMLEAK_CHECK_AS_DLL 
#endif

#ifdef INCLUDE_MEMLEAK_CHECK_AS_DLL // older version
 #define LINK_MEMLEAK_CHECK_AS_DLL 
#endif


/* If using MemLeakCheck as a dll, define
 * LINK_MEMLEAK_CHECK_AS_DLL
 *
 * The settings must also match the dll.
 *
 * for memleakcheckta.dll (thread safe auto free only dll)
 * define cl_mem_leak_check 
 *
 * for memleakcheckt.dll (thread safe diagnostics dll)
 * you must also define cl_mem_leak_diagnostics
 *
 *
 * define code_uses_redefined_malloc to use the 
 * version where you 
 * do a search and replace of source code
 * of malloc to _malloc_, calloc to _calloc_
 * realloc to _realloc_ and strdup to _strdup_
 * - advantage, don't need to make sure this header
 * is included after all the standard libraries
 * when including it as source code in your build.
 *
 * define cl_MSVC_debug_mem_leak_tests 
 * if you want to revert to the MSVC debug version
 * - not needed if you use it as a dll
 *
 * define cl_mlc_qsort_pointers if you want the pointers
 * sorted according to the files and line numbers where
 * the allocation occured.
 * the qsort is done just before the diagnostics report (if in diagnostic mode)
 * It will be slow if you have many unfreed pointers, e.g. thousands
 * of them. Sorts the pointers by file, then line number, then size
 *
 *
 * To switch on the automatic freeing of memory on exit
 * define cl_mem_leak_check 
 *
 * To also show diagnostics, 
 * define cl_mem_leak_diagnostics
 *
 * To free any remaining unfreed memory (e.g. on exit)
 * call MemLeakCheckAndFree  * in your program. 
 * This is also the routine that hows the diagnostics report if
 * any memory needed to be freed.
 *
 * The default diagnostics warnings proc writes to stderr, which is probably going to
 * be fine for many Console apps but it's not suitable for Windows programs
 *
 * To set your own warnings proc, use  SetMemLeakWarnProc(proc)
 *
 * Here is a windows version which just shows the warnings in your debugger output window:

#ifdef cl_mem_leak_diagnostics

MemLeakWarnProc(char *szmsg)
{
 OutputDebugString(szmsg); 
}

#endif
...
SetMemLeakWarnProc(MemLeakWarnProc);
...

 *
 *And here is a version which shows a message box
 *
 
#ifdef cl_mem_leak_diagnostics

void MemLeakWarnProc(char *szmsg)
{
 MessageBox(NULL,szmsg,"Memory leak detected!",MB_ICONEXCLAMATION);
}
#endif
...
SetMemLeakWarnProc(MemLeakWarnProc);
...

 *
 * When the pointers get freed at the end you may get a whole succession
 * of warnings. Again that works fine for a console app though if there 
 * are many one may want to log them to a file instead. It doesn't work in 
 * well in Windows gui programs and you will probably want to log them to a file
 * in that case, or send them to your debugger output window (see above).
 *
 * To log them to a file, set a MemLeakLog proc which could be done like this for a windows app:
 *

#ifdef cl_mem_leak_diagnostics
void MemLeakLogProc(char *szmsg)
{
 static char init;
 char *szLog="MemLeakLog.txt";
 FILE *fp=fopen(szLog,"a");
 if(!init)
 {
  char szinfo[1024];
  sprintf(szinfo,"Memory leak(s) detected and logged to %s - see this for the details"
         ,szLog
         );
  MessageBox(NULL,szinfo,"Memory leak(s) detected!",MB_ICONEXCLAMATION);
 }
 if(fp)
 {
  if(!init)
  {
   // start a new section in the memory diagnostics log - captioned 
   // with the current date and time
   struct tm *today;
   char tmpbuf[128];
   time_t time_now;
   time( &time_now );
   today = localtime( &time_now );
   strftime( tmpbuf, 128,"%#I %p %A, %B %d, %Y %Z",today);
   fprintf
    (fp
    ,"\n"
     "************************************************************\n"
     "%s\n"
     "************************************************************\n"
     "\n\n"
    ,tmpbuf
    );
  }
  fprintf(fp,"%s",szmsg);
  fclose(fp);
 }
 init=1;
}
#endif

...
SetMemLeakLogProc(MemLeakLogProc);
...

 * If you don't set a MemLeakLogProc then your MemLeakWarnProc gets used for it
 *
 * You can also use SetMemLeakAnticipateFreeProc
 * - this just gets called with the file and line number
 * before every free that occurs in your program
 * - but may be useful sometimes in debugging
 * - can be used to print a log of all the frees
 * so you can easily find the last one  that 
 * was just about to be called before a crash
 *
 * As it is this code isn't thread safe. However if you are using it
 * in windows you can make it thread safe by defining
 * MAKE_WINDOWS_THREAD_SAFE
 * and link with kernel32.lib - as you will do anyway probably 
 * if you build it in a Windows compiler.
 *
 * This works by using a busy wait on InterlockedExchange(..)
 * This is fast if you normally expect to be able to gain access
 * or to wait only a short time. Even if there is thread contention
 * e.g. because one thread is allocating a large area of memory,
 * the busy wait only does a few assembly language isntructions before
 * ceding the rest of its time slice back to the operating system
 * using Sleep(0);
 *
 * If you want to not use this any more and change to
 * the MSVC memory leak diagnostics with malloc_dbg
 * etc, but retain this header, then use define cl_MSVC_debug_mem_leak_tests
 *
 * You can also use it as a dll - which doesn't require
 * any changes to your source code apart from including
 *.the header and linking to the dll.
 *
 * To use it as a dll, you need to define
 * LINK_MEMLEAK_CHECK_AS_DLL
 * in the version of the header used in your program.
 *
 * The effect of this is that in your program
 * wherever it has a call to malloc, calloc, realloc
 * strdup or free, then this will be replaced by calls
 * to the routines xz_malloc etc in the dll. Those then
 * in turn will call the real malloc, calloc etc.
 * as needed
 *
 * You can also set your own routines for allocation and
 * reallocation etc.
 *
 * Place the following code at the end of one of your source files
 * where the #undefs won't affect any other memory
 * allocation calls you make, and call it before you
 * do any allocations
 *

#ifdef cl_mem_leak_diagnostics

 // If you need to call the standard 
 // malloc or calloc etc here then undefine them first
 // as they got redefined to _malloc_ etc in the header
#undef malloc
#undef calloc
#undef realloc
#undef memcpy
#undef free
#include "malloc.h"
void init_memleakcheck_alloc(void)
{
 // now here set whatever allocation functions you actually
 // use
  set_mlc_malloc(malloc);
  set_mlc_calloc(calloc);
  set_mlc_realloc(realloc);
  set_mlc_free(free);
  set_mlc_memcpy(memcpy);
  set_mlc_strlen(strlen);
  set_mlc_strcpy(strcpy);
}
#endif

 * and call it when your program starts, or first needs to use
 * MemLeakCheck
 *
 * It may be an idea to do this anyway in case your app and the dll
 * don't use the same version of the C run time library
 * (they only use the same version if they are both the same as regards
 * single / multi-threaded and release / debug, and also the same
 * in terms of whether they are made in MSVC 4.2 or later or not.
 *
 * You should  use a dll built with the same settings for diagnostics
 * and auto free as your app.
 *
 * Zip includes memleakcheckdmt for diagnostics - compiled to be thread 
 * safe and can be used with multi-threaded apps as well as single 
 * threaded ones - and uses the CRT for multithreaded debug apps.
 * You don't need to do the init_memleakcheck_alloc(..)
 * if using it with a multithreaded debug build made in a version of
 * MSVC later than 4.0 as you will be using the same CRT.. 
 *
 * For auto free it includes memleakcheckat - thread safe
 * multi-threaded release build.
 *
 * To build the dll - in MSVC you go to File | New | Projects
 * | Win 32 dynamic link library, make an empty DLL, then add the MemLeakCheck
 * files to your project
 *
 * To build it you use:
 * 
 * #define BUILD_MEMLEAK_CHECK_AS_DLL
 *
 * Suggestion for dll version names:
 *
 * If windows thread safe add a t at the end
 * memleakcheckt.dll.
 * if it is for
 * auto free only, add an a.
 * memleakcheckat.dll.
 * If the variables are thread local, so each thread
 * needs to call the auto free separately, add an l
 * memleakcheckalt.dll.
 * If it is a debug build add a d
 * memleakcheckadlt.dll etc.
 * order
 *
 * The zip you can download from the home page
 * has memleakcheckt.dll and memleakcheckat.dll
 *
 * If you include this source code as part of a larger dll
 * then be sure that you don't export MemLeakCheckAndFree under its 
 * original name, or indeed any of the other routines in 
 * this header (unless your dll is intended as a replacemnt for this
 * one).
 *
 * Instead, if you want to
 * export one of the routines, first provide a wrapper routine
 * for it with some other name perhaps prefixed by the name
 * of your dll - e.g. WinMing_MemLeakCheckAndFree
 * which will call your local version of MemLeakCheckAndFree 
 * to check for memory leaks and free memory for your dll .
 *
 * This will ensure that users of your dll can continue to
 * use any of the original MemLeakCheckAndFree dlls 
 * in their own apps as well as the version for freeing memory in
 * your dll, and the two won't get mixed up.
 */

#if defined(BUILD_MEMLEAK_CHECK_AS_DLL)||defined(LINK_MEMLEAK_CHECK_AS_DLL)

 #ifdef __cplusplus
  #define MEMLEAK_CHECK_WM_DLL_A extern "C" __declspec (dllexport)
 #else
  #define MEMLEAK_CHECK_WM_DLL_A __declspec (dllexport)
 #endif

 #define MEMLEAK_CHECK_WM_DLL_B    __stdcall

#else // ndef compile_as_DLL

 #define MEMLEAK_CHECK_WM_DLL_A
 #define MEMLEAK_CHECK_WM_DLL_B

#endif


#ifdef LINK_MEMLEAK_CHECK_AS_DLL

 MEMLEAK_CHECK_WM_DLL_A void MEMLEAK_CHECK_WM_DLL_B set_mlc_malloc
 (void *(__cdecl *new_mlc_malloc)(size_t size));

 MEMLEAK_CHECK_WM_DLL_A void MEMLEAK_CHECK_WM_DLL_B set_mlc_calloc
 (void *(__cdecl *new_mlc_calloc)(size_t num,size_t size));

 MEMLEAK_CHECK_WM_DLL_A void MEMLEAK_CHECK_WM_DLL_B set_mlc_realloc
 (void *(__cdecl *new_mlc_realloc)(void *p,size_t size));

 MEMLEAK_CHECK_WM_DLL_A void MEMLEAK_CHECK_WM_DLL_B set_mlc_free
 (void (__cdecl *new_mlc_free)(void *p));

 MEMLEAK_CHECK_WM_DLL_A void MEMLEAK_CHECK_WM_DLL_B set_mlc_memcpy
 (void *(__cdecl *new_mlc_memcpy)(void *dest,void *src,size_t count));

 MEMLEAK_CHECK_WM_DLL_A void MEMLEAK_CHECK_WM_DLL_B set_mlc_strlen
 (size_t (__cdecl *new_mlc_strlen)(const char *string));

 MEMLEAK_CHECK_WM_DLL_A void MEMLEAK_CHECK_WM_DLL_B set_mlc_strcpy
 (char *(__cdecl *new_mlc_strcpy)(char *strDestination,const char *strSource ));
#endif

// If  one builds the library without cl_mem_leak_check defined
// code that calls these routines will still link okay but do nothing

#ifndef cl_mem_leak_check

 #define MemLeakCheckAndFree() 0
 #define SetMemLeakWarnProc()
 #define SetMemLeakLogProc()
 #define SetMemLeakAnticipateFreeProc()

#endif

#ifndef cl_mem_leak_diagnostics

 #define SetMemLeakLogProc()
 #define SetMemLeakWarnProc()
 #define SetMemLeakAnticipateFreeProc()

#endif

#ifndef _SIZE_T_DEFINED
typedef unsigned int size_t;
#define _SIZE_T_DEFINED
#endif

#ifdef cl_mem_leak_check

 MEMLEAK_CHECK_WM_DLL_A int MEMLEAK_CHECK_WM_DLL_B MemLeakCheckAndFree(void);

 #ifdef cl_mem_leak_diagnostics

  MEMLEAK_CHECK_WM_DLL_A void MEMLEAK_CHECK_WM_DLL_B SetMemLeakWarnProc(void (__cdecl *NewMemLeakWarnProc)(char *szmsg));
  MEMLEAK_CHECK_WM_DLL_A void MEMLEAK_CHECK_WM_DLL_B SetMemLeakLogProc(void (__cdecl *NewMemLeakLogProc)(char *szmsg));

  MEMLEAK_CHECK_WM_DLL_A void MEMLEAK_CHECK_WM_DLL_B SetMemLeakAnticipateFreeProc(void( __cdecl *NewMemLeakAnticipateFreeProc)(char *szFile,int nLine));
  MEMLEAK_CHECK_WM_DLL_A void * MEMLEAK_CHECK_WM_DLL_B xz_realloc(void *p,size_t size,char *szFile,int iLine);
  MEMLEAK_CHECK_WM_DLL_A void * MEMLEAK_CHECK_WM_DLL_B xz_malloc(size_t size,char *szFile,int iLine);
  MEMLEAK_CHECK_WM_DLL_A void * MEMLEAK_CHECK_WM_DLL_B xz_calloc(size_t num,size_t size,char *szFile,int iLine);
  MEMLEAK_CHECK_WM_DLL_A void MEMLEAK_CHECK_WM_DLL_B xz_free(void *p,char *szFile,int iLine);
  MEMLEAK_CHECK_WM_DLL_A char * MEMLEAK_CHECK_WM_DLL_B xz_strdup(const char *src,char *szFile,int iLine);
  #define cl_xz_strdup 

  #define _malloc_(size) xz_malloc(size,__FILE__,__LINE__)
  #define _calloc_(num,size) xz_calloc(num,size,__FILE__,__LINE__)
  #define _realloc_(p,size) xz_realloc(p,size,__FILE__,__LINE__)
  #define _strdup_(src) xz_strdup(src,__FILE__,__LINE__)
  #define _free_(p) xz_free(p,__FILE__,__LINE__)

 #else //  ndef cl_mem_leak_diagnostics
  // This is the version that auto frees unfreed memory but with no diagnostics

  MEMLEAK_CHECK_WM_DLL_A void * MEMLEAK_CHECK_WM_DLL_B xz_realloc(void *p,size_t size);
  MEMLEAK_CHECK_WM_DLL_A void * MEMLEAK_CHECK_WM_DLL_B xz_malloc(size_t size);
  MEMLEAK_CHECK_WM_DLL_A void * MEMLEAK_CHECK_WM_DLL_B xz_calloc(size_t num,size_t size);
  MEMLEAK_CHECK_WM_DLL_A void MEMLEAK_CHECK_WM_DLL_B xz_free(void *p);
  MEMLEAK_CHECK_WM_DLL_A char * MEMLEAK_CHECK_WM_DLL_B xz_strdup(const char *src); 
  #define cl_xz_strdup

  #define _malloc_ xz_malloc
  #define _free_ xz_free
  #define _calloc_ xz_calloc
  #define _realloc_ xz_realloc

  #define _strdup_ xz_strdup

 #endif // ndef cl_mem_leak_diagnostics

 #ifndef code_uses_redefined_malloc

  #define  malloc _malloc_
  #define  free  _free_ 
  #define  calloc _calloc_
  #define  realloc _realloc_
  #define  strdup _strdup_

  // if you want to use the redefined malloc, then in your code you should
  // use _malloc_ etc instead of malloc
  // - useful if it is inconvenient to place this
  // header after all the standard libraries and before
  // the source code that uses allocation.

 #endif

#else // ndef cl_mem_leak_check

 #ifdef cl_MSVC_debug_mem_leak_tests 

  #define _malloc_(size) _malloc_dbg(size, _CLIENT_BLOCK, __FILE__, __LINE__ )
  #define _free_(p) _free_dbg(p,_CLIENT_BLOCK);
  #define _calloc_(num,size) _calloc_dbg(num,size, _CLIENT_BLOCK, __FILE__, __LINE__ )
  #define _realloc_(userData,size) _realloc_dbg(userData,size, _CLIENT_BLOCK, __FILE__, __LINE__ )
  #define _strdup_(src) xz_strdup(src)
  MEMLEAK_CHECK_WM_DLL_A char * MEMLEAK_CHECK_WM_DLL_B xz_strdup(const char *src); // still need it for the malloc debug
  #define cl_xz_strdup

 #else // ndef cl_MSVC_debug_mem_leak_tests and also ndef cl_mem_leak_check
       // i.e. just use normal malloc etc

  #ifdef code_uses_redefined_malloc

   #define  _malloc_ malloc
   #define  _free_  free
   #define  _calloc_ calloc
   #define  _realloc_ realloc
   #define  _strdup_ strdup

  #endif


 #endif // ndef cl_MSVC_debug_mem_leak_tests 

#endif // ndef cl_mem_leak_check

#endif /* MEMLEAKCHECK_H_INCLUDED */

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here


Written By
Web Developer
United Kingdom United Kingdom
Trained as a mathematician.

Now I program shareware apps - music and 3D. Main ones so far, Fractal Tune Smithy (music) and Virtual Flower (3D / VRML).

Comments and Discussions