cvmalloc.cc
// $Id$
/* Package to check for memory leaks. The first time a program
* calls malloc, this program sets up to intercept all calls to
* malloc() and free(), and reports any problems at exit. Based
* (loosely!) on sample code given at
* www.gnu.org/manual/glibc-2.2.3/html_node/libc_33.html.
*
* $Log$
*
*/
#include <stdio.h>
#include <stdlib.h>
#include <malloc.h> // This must go last to pick up __malloc_hook
// and __free_hook.
#include "cvmalloc.h" // Prototypes for malloc_check() and
// getOutstandingMallocs()
// Tyepdefs and prototypes for hooks
// ------------------------------------------------------------------
static void cv_init_hook ( void );
typedef void *m_hook( size_t, const void * );
static m_hook cv_malloc_hook;
static m_hook *sys_malloc_hook;
typedef void f_hook( void *, const void * );
static f_hook cv_free_hook;
static f_hook *sys_free_hook;
// Data structures for checking for leaks/errors.
// ------------------------------------------------------------------
static long numMalloc = 0;
static long numFree = 0;
struct node_t
{
void *ptr;
size_t size;
const void *caller;
node_t *next;
} *head = 0;
// Override initializing hook from the C library.
// ------------------------------------------------------------------
void (*__malloc_initialize_hook) (void) = cv_init_hook;
// cv_init_hook()
// ------------------------------------------------------------------
/*
* This function gets called when the application does its first
* malloc, and sets up for the others to be called.
*/
static void
cv_init_hook (void)
{
if ( -1 == atexit( malloc_check ) )
{
perror( "cv_init_hook: atexit" );
exit( 1 );
}
// Capture system hooks
sys_malloc_hook = __malloc_hook;
sys_free_hook = __free_hook;
// Install hooks for this module
__malloc_hook = cv_malloc_hook;
__free_hook = cv_free_hook;
}
// cv_malloc_hook()
// ------------------------------------------------------------------
/*
* Called whenever the application calls malloc().
*/
static void *
cv_malloc_hook (size_t size, const void *caller)
{
numMalloc++;
void *result;
// Reinstall system hooks so malloc/free in the following code
// will not be intercepted recursively.
__malloc_hook = sys_malloc_hook;
__free_hook = sys_free_hook;
// Do the malloc and record the result in our linked list.
result = malloc( size );
#ifdef DEBUG
fprintf ( stderr, "malloc(%u) returns %p\n", (size_t) size, result);
#endif
if ( !head )
{
head = (node_t*)malloc( sizeof( node_t ) );
head->ptr = result;
head->size = size;
head->caller = caller;
head->next = 0;
}
else
{
node_t *nxt = (node_t*) malloc( sizeof( node_t ) );
nxt->ptr = result;
nxt->size = size;
nxt->next = head;
nxt->caller = caller;
head = nxt;
}
/* Restore our own hooks */
__malloc_hook = cv_malloc_hook;
__free_hook = cv_free_hook;
return result;
}
// cv_free_hook()
// ------------------------------------------------------------------
/*
* Called whenever the application calls free().
*/
static void
cv_free_hook (void *ptr, const void *caller)
{
numFree++;
// Install system hooks so malloc/free in the following code will
// not be intercepted recursively.
__malloc_hook = sys_malloc_hook;
__free_hook = sys_free_hook;
// Make sure the ptr being freed is in our list of recognized
// pointers.
node_t *nxt = head;
node_t *prev = 0;
bool found = false;
while ( nxt )
{
if ( ptr == nxt->ptr )
{
found = true;
if (prev)
{
// Remove from middle of list
prev->next = nxt->next;
free( nxt );
}
else
{
// Remove from head of list.
head = head->next;
}
break;
}
prev = nxt;
nxt = nxt->next;
}
if ( !found )
{
fprintf( stderr, "*** Attempt to free location %p, which was not"
" allocated by malloc.\n", ptr );
exit( 1 );
}
// The ptr is OK, let the free be handled by the system
#ifdef DEBUG
fprintf( stderr, "free( %p )\n", ptr );
#endif
free (ptr);
// Restore hooks to intercept malloc/free again
__malloc_hook = cv_malloc_hook;
__free_hook = cv_free_hook;
}
// malloc_check()
// ------------------------------------------------------------------
/*
* Invoked when program exits to check malloc/free usage.
* May also be called directly from an application during run time
* to see if the list of allocations is growing.
*/
void
malloc_check( void )
{
fprintf( stderr, "Total calls to malloc(): %8ld\n"
"Total calls to free(): %8ld\n",
numMalloc, numFree );
node_t *nxt = head;
if (head)
{
fprintf( stderr,
"*** The following allocations were not freed:\n" );
while ( nxt )
{
fprintf(stderr, " %d bytes at %p called from %p \n",
nxt->size, nxt->ptr, nxt->caller );
nxt = nxt->next;
}
}
}
// getOutstandingMallocs()
// -------------------------------------------------------------------
/*
* Returns the number of mallocs for which there have been no
* corresponding frees yet. Can be called as an application runs;
* if this number continues to increase, it indicates a memory leak.
*/
int
getOutstandingMallocs( void )
{
return numMalloc;
}