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;
       }