I found that a lot of the leak finders out there, simply sucked. They tried to hard to overcomplicate things or redefined functions (which didn't work here) and their output was hardly concise.
This leak finder I wrote in ~35 minutes including research time tracks all calls to its internal functions iMalloc and iFree, and so can identify memory freed erroneously and also lists all unfreed memory on request. Even if memory leaks aren't a major issue, this can help identify bugs as it presents the file and line on which the unallocated memory was freed and the file and line on which memory was not freed.
To install, include leak.h. Change your functions to iMalloc and iFree, and include a CheckLeaks() on close. To enable the debugging, #define DEBUG. There is also a little xprintf function included which is identical to printf in usage but on C99 systems switches on and off with the memory checker, so debug messages are hidden when the program is not in debug mode.
leak.h
void * LogMalloc( size_t stSize, char * cFile, unsigned int uiLine );
void LogFree( void * vPoint, char * cFile, unsigned int uiLine );
#ifdef DEBUG
#define iMalloc( size ) LogMalloc( size, __FILE__, __LINE__ )
#define iFree( pointer ) LogFree( pointer, __FILE__, __LINE__ )
#define xprintf( format, ... ) printf( format, ##__VA_ARGS__ )
unsigned int uiMemSpaces = 0;
char ** cMemFiles = NULL;
unsigned int * uiMemLines = NULL;
void ** vMemSpaces = NULL;
#include "leak.c"
#else
#define iMalloc( size ) malloc( size )
#define iFree( pointer ) free( pointer )
#define xprintf( format, ... )
#endif
leak.c
void CheckLeaks( void )
{
if( uiMemSpaces )
{
xprintf( "%d memory leaks found.\n", uiMemSpaces );
unsigned int uiMyLeak = 0;
while( uiMyLeak < uiMemSpaces )
{
printf( "%d: %p, from line %d of %s.\n", uiMyLeak, vMemSpaces[ uiMyLeak ], uiMemLines[ uiMyLeak ], cMemFiles[ uiMyLeak ] );
free( vMemSpaces[ uiMyLeak ] );
uiMyLeak++;
}
free( cMemFiles );
free( uiMemLines );
free( vMemSpaces );
}
else
{
xprintf( "No leaks detected. Congratulations.\n" );
}
}
void * LogMalloc( size_t stSize, char * cFile, unsigned int uiLine )
{
void * vPoint = malloc( stSize );
if( vPoint != NULL )
{
unsigned int * uiNewMemLines = ( unsigned int * ) malloc( sizeof( unsigned int ) * ( uiMemSpaces + 1 ) );
if( uiNewMemLines == NULL )
{
xprintf( "Not enough memory for allocation.\n" );
return vPoint;
}
char ** cNewMemFiles = ( char ** ) malloc( sizeof( char * ) * ( uiMemSpaces + 1 ) );
if( cNewMemFiles == NULL )
{
free( uiNewMemLines );
xprintf( "Not enough memory for allocation.\n" );
return vPoint;
}
void ** vNewMemSpaces = ( void ** ) malloc( sizeof( void * ) * ( uiMemSpaces + 1 ) );
if( vNewMemSpaces == NULL )
{
free( uiNewMemLines );
free( cNewMemFiles );
xprintf( "Not enough memory for allocation.\n" );
return vPoint;
}
if( uiMemSpaces )
{
memcpy( uiNewMemLines, uiMemLines, sizeof( unsigned int ) * uiMemSpaces );
memcpy( vNewMemSpaces, vMemSpaces, sizeof( void * ) * uiMemSpaces );
memcpy( cNewMemFiles, cMemFiles, sizeof( char * ) * uiMemSpaces );
free( uiMemLines );
free( vMemSpaces );
free( cMemFiles );
}
uiMemLines = uiNewMemLines;
vMemSpaces = vNewMemSpaces;
cMemFiles = cNewMemFiles;
uiMemLines[ uiMemSpaces ] = uiLine;
vMemSpaces[ uiMemSpaces ] = vPoint;
cMemFiles[ uiMemSpaces ] = cFile;
uiMemSpaces++;
}
return vPoint;
}
void LogFree( void * vPoint, char * cFile, unsigned int uiLine )
{
unsigned int uiMySpace = 0;
while( uiMySpace < uiMemSpaces )
{
if( vPoint == vMemSpaces[ uiMySpace ] )
{
free( vPoint );
if( uiMemSpaces == 1 )
{
free( uiMemLines );
free( cMemFiles );
free( vMemSpaces );
}
else
{
unsigned int * uiNewMemLines = ( unsigned int * ) malloc( sizeof( unsigned int ) * ( uiMemSpaces - 1 ) );
if( uiNewMemLines == NULL )
{
xprintf( "Not enough memory to remove lines entry... unstable!\n" );
return;
}
char ** cNewMemFiles = ( char ** ) malloc( sizeof( char * ) * ( uiMemSpaces - 1 ) );
if( cNewMemFiles == NULL )
{
free( uiNewMemLines );
xprintf( "Not enough memory to remove files entry... unstable!\n" );
return;
}
void ** vNewMemSpaces = ( void ** ) malloc( sizeof( void * ) * ( uiMemSpaces - 1 ) );
if( vNewMemSpaces == NULL )
{
free( uiNewMemLines );
free( cNewMemFiles );
xprintf( "Not enough memory to remove files entry... unstable!\n" );
return;
}
if( uiMySpace )
{
memcpy( vNewMemSpaces, vMemSpaces, sizeof( void * ) * uiMySpace );
memcpy( cNewMemFiles, cMemFiles, sizeof( char * ) * uiMySpace );
memcpy( uiNewMemLines, uiMemLines, sizeof( unsigned int ) * uiMySpace );
}
if( uiMySpace < uiMemSpaces - 1 )
{
memcpy( &vNewMemSpaces[ uiMySpace ], &vMemSpaces[ uiMySpace + 1 ], sizeof( void * ) * ( ( uiMemSpaces - uiMySpace ) - 1 ) );
memcpy( &cNewMemFiles[ uiMySpace ], &cMemFiles[ uiMySpace + 1 ], sizeof( char * ) * ( ( uiMemSpaces - uiMySpace ) - 1 ) );
memcpy( &uiNewMemLines[ uiMySpace ], &uiMemLines[ uiMySpace + 1 ], sizeof( unsigned int ) * ( ( uiMemSpaces - uiMySpace ) - 1 ) );
}
free( vMemSpaces );
free( cMemFiles );
free( uiMemLines );
vMemSpaces = vNewMemSpaces;
cMemFiles = cNewMemFiles;
uiMemLines = uiNewMemLines;
}
uiMemSpaces--;
return;
}
uiMySpace++;
}
xprintf( "Warning, %s @ %d tried to free memory @ %p that wasn't registered.\n", cFile, uiLine, vPoint );
return;
}
Any bugs, mistakes or inconsistencies are welcome.
Shouldn't the dropdown box read C++/C and not C++/C#? C# has its own option!
Athlon64 2.7gHz->OC 3.9gHz, 31C, MSi 9500GT->OC 1gHz core/2gHz memory, 48C, 4Gb DDR2 667, 500Gb Seagate + 80Gb Maxtor + 40Gb Maxtor = 620Gb, XP Home
Air cooled, total cost £160