Share via


_CrtSetAllocHook

Installs a client-defined allocation function by hooking it into the C run-time debug memory allocation process (debug version only).

_CRT_ALLOC_HOOK_CrtSetAllocHook(_CRT_ALLOC_HOOKallocHook**);**

Routine Required Header Compatibility
_CrtSetAllocHook <crtdbg.h> Win NT, Win 95

For additional compatibility information, see Compatibility in the Introduction.

Libraries

LIBCD.LIB Single thread static library, debug version
LIBCMTD.LIB Multithread static library, debug version
MSVCRTD.LIB Import library for MSVCRTD.DLL, debug version

Return Value

_CrtSetAllocHook returns the previously defined allocation hook function.

Parameter

allocHook

New client-defined allocation function to hook into the C run-time debug memory allocation process

Remarks

_CrtSetAllocHook allows an application to hook its own allocation function into the C run-time debug library memory allocation process. As a result, every call to a debug allocation function to allocate, reallocate, or free a memory block triggers a call to the application’s hook function. _CrtSetAllocHook provides an application with an easy method for testing how the application handles insufficient memory situations, the ability to examine allocation patterns, and the opportunity to log allocation information for later analysis. When _DEBUG is not defined, calls to _CrtSetAllocHook are removed during preprocessing.

The _CrtSetAllocHook function installs the new client-defined allocation function specified in allocHook and returns the previously defined hook function. The following example demonstrates how a client-defined allocation hook should be prototyped:

int YourAllocHook( int allocType, void *userData, size_t size, int blockType,
   long requestNumber, const unsigned char *filename, int lineNumber);

The allocType argument specifies the type of allocation operation (_HOOK_ALLOC, _HOOK_REALLOC, _HOOK_FREE) that triggered the call to the allocation’s hook function. When the triggering allocation type is _HOOK_FREE, userData is a pointer to the user data section of the memory block about to be freed. However, when the triggering allocation type is _HOOK_ALLOC or _HOOK_REALLOC, userData is NULL because the memory block has not been allocated yet.

size specifies the size of the memory block in bytes, blockType indicates the type of the memory block, requestNumber is the object allocation order number of the memory block, and if available, filename and lineNumber specify the source file name and line number where the triggering allocation operation was initiated.

After the hook function has finished processing, it must return a Boolean value, which tells the main C run-time allocation process how to continue. When the hook function wants the main allocation process to continue as if the hook function had never been called, then the hook function should return TRUE. This causes the original triggering allocation operation to be executed. Using this implementation, the hook function can gather and save allocation information for later analysis, without interfering with the current allocation operation or state of the debug heap.

When the hook function wants the main allocation process to continue as if the triggering allocation operation was called and it failed, then the hook function should return FALSE. Using this implementation, the hook function can simulate a wide range of memory conditions and debug heap states to test how the application handles each situation.

For more information about how _CrtSetAllocHook can be used with other memory management functions or how to write your own client-defined hook functions, see Writing Your Own Debugging Hook Functions.

Example

/*****************************************************************
 *  EXAMPLE  2                                                   *
 *  ----------                                                   *
 *  This program illustrates several ways to use debugging hook  *
 *  functions with the new debug versions of the C runtime       *
 *  libraries. To add some realism, it has a few elements of an  *
 *  actual application, including two bugs.                      *
 *                                                               *
 *  The program stores birthdate information in a linked list    *
 *  of Client blocks. A Client-dump hook function validates the  *
 *  birthday data and reports the contents of the Client blocks. *
 *  An allocation hook function logs heap operations to a text   *
 *  file, and the report hook function logs reports to the same  *
 *  text file.                                                   *
 *                                                               *
 *  NOTE: The allocation hook function explicitly excludes CRT   *
 *        blocks (the memory allocated internally by the C       *
 *        runtime library) from its log. It is important to      *
 *        understand why! The hook function uses fprintf( ) to   *
 *        write to the log file, and fprintf( ) allocates a CRT  *
 *        block. If CRT blocks were not excluded in this case,   *
 *        an endless loop would be created in which fprintf( )   *
 *        would cause the hook function to be called, and the    *
 *        hook would in turn call fprintf( ), which would cause  *
 *        the hook to be called again, and so on. The moral is:  *
 *                                                               *
 *   -->  IF YOUR ALLOCATION HOOK USES ANY C RUNTIME FUNCTION    *
 *        THAT ALLOCATES MEMORY, THE HOOK MUST IGNORE CRT-TYPE   *
 *        ALLOCATION OPERATIONS!                                 *
 *                                                               *
 *  HINT: If you want to be able to report CRT-type blocks in    *
 *        your allocation hook, use Windows API functions for    *
 *        formatting and output, instead of C runtime functions. *
 *        Since the Windows APIs do not use the CRT heap, they   *
 *        will not trap your hook in an endless loop.            *
 *                                                               *
 *  BUGS: There are two bugs in the program below, which the     *
 *        debug heap features identify in several ways. One bug  *
 *        is that the birthDay.Name field is not large enough    *
 *        to hold several of the test names. The field should    *
 *        be larger, and strncpy( ) should be used in place of   *
 *        strcpy( ). The second bug is that the while( ) loop    *
 *        in the printRecords( ) function should not end until   *
 *        HeadPtr itself == NULL. This bug results not only in   *
 *        an incomplete display of birthdays, but also in a      *
 *        memory leak. In addition to these two bugs, Gauss'     *
 *        birthday data is out of range (April 30, not 32).      *
 *                                                               *
 *****************************************************************/

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <malloc.h>
#include <time.h>
#include <crtdbg.h>

/*****************************************************************
 *  DATA DECLARATIONS AND DEFINES                                *
 *****************************************************************/

// The following arrays provide test data for the example program:
const char * Names[] =
{
   "George Washington",
   "Thomas Jefferson",
   "Carl Friedrich Gauss",
   "Ludwig van Beethoven",
   "Thomas Carlyle"
} ;

const int Dates[] =
{
   1732,  2, 11,
   1743,  4, 13,
   1777,  4, 32,
   1795, 12,  4,
   1770, 12, 16
} ;

#define  TEST_RECS            5
// A generic sort of linked-list data structure, in this case for birthdays:
typedef struct BirthdayStruct
{
   struct BirthdayStruct * NextRec;
   int   Year;
   int   Month;
   int   Day;
   char  Name[20];
} birthDay;

birthDay * HeadPtr;
birthDay * TailPtr;

#define  FILE_IO_ERROR        0
#define  OUT_OF_MEMORY        1

#define  TRUE                 7
#define  FALSE                0

// Macros for setting or clearing bits in the CRT debug flag
#ifdef _DEBUG
#define  SET_CRT_DEBUG_FIELD(a)   _CrtSetDbgFlag((a) | _CrtSetDbgFlag(_CRTDBG_REPORT_FLAG))
#define  CLEAR_CRT_DEBUG_FIELD(a) _CrtSetDbgFlag(~(a) & _CrtSetDbgFlag(_CRTDBG_REPORT_FLAG))
#else
#define  SET_CRT_DEBUG_FIELD(a)   ((void) 0)
#define  CLEAR_CRT_DEBUG_FIELD(a) ((void) 0)
#endif


/*****************************************************************
 *  SPECIAL-PURPOSE ROUTINES                                     *
 *****************************************************************/

/* ERROR HANDLER
   -------------
   Handling serious errors gracefully is a real test of craftsmanship.
   This function is just a stub; it doesn't really handle errors.
*/
void FatalError( int ErrType )
{
   exit( 1 );
}


/* MEMORY ALLOCATION FUNCTION
   --------------------------
   The createRecord function allocates memory for a new birthday record,
   fills in the structure members, and then adds the record to a linked list.
   In debug builds, it makes these allocations in Client blocks. If memory
   is not available, it calls the error handler.
*/
void createRecord(
   const int    Year,
   const int    Month,
   const int    Day,
   const char * Name
#ifdef _DEBUG
,  const unsigned char * szFileName, int nLine
#endif
   )
{
   birthDay * ptr;
   size_t n;

   n = sizeof( struct BirthdayStruct );
   ptr = (birthDay *) _malloc_dbg( n, _CLIENT_BLOCK, szFileName, nLine );
   if( ptr == NULL )
      FatalError( OUT_OF_MEMORY );
   ptr->Year = Year;
   ptr->Month = Month;
   ptr->Day = Day;
   strcpy( ptr->Name, Name );

   ptr->NextRec = NULL;
   if ( HeadPtr == NULL )     // If this is the first record in the linked list
      HeadPtr = ptr;
   else
      TailPtr->NextRec = ptr;
   TailPtr = ptr;
}


/* BIRTHDAY DISPLAY FUNCTION
   -------------------------
   This function traverses the linked list, displays the birthday data,
   and then frees the memory blocks used to store the birthdays.
*/
void printRecords( )
{
   birthDay * ptr;
   char *months[] = {
      "", "January", "February", "March", "April", "May", "June", "July",
      "August", "September", "October", "November", "December" };

   if ( HeadPtr == NULL )        // Do nothing if list is empty
      return;

   printf( "\n\nThis is the birthday list:\n" );
   while ( HeadPtr->NextRec != NULL )
   {
      printf( "   %s was born on %s %d, %d.\n",
              HeadPtr->Name, months[HeadPtr->Month], HeadPtr->Day, HeadPtr->Year );
      ptr = HeadPtr->NextRec;
      _free_dbg( HeadPtr, _CLIENT_BLOCK );
      HeadPtr = ptr;
   }
}


/*****************************************************************
 *  DEBUG C RUNTIME LIBRARY HOOK FUNCTIONS AND DEFINES           *
 *****************************************************************/
#ifdef _DEBUG
#define  createRecord(a, b, c, d) \
         createRecord(a, b, c, d, __FILE__, __LINE__)
FILE *logFile;                // Used to log allocation information
const char lineStr[] = { "---------------------------------------\
--------------------------------------\n"  };

/* CLIENT DUMP HOOK FUNCTION
   -------------------------
   A hook function for dumping a Client block usually reports some
   or all of the contents of the block in question.  The function
   below also checks the data in several ways, and reports corruption
   or inconsistency as an assertion failure.
*/
void __cdecl MyDumpClientHook(
   void * pUserData,
   size_t nBytes
   )
{
   birthDay * bday;

   bday = (birthDay *) pUserData;

   _RPT4( _CRT_WARN, "   The birthday of %s is %d/%d/%d.\n",
          bday->Name, bday->Month, bday->Day, bday->Year );
   _ASSERTE( ( bday->Day > 0 ) && ( bday->Day < 32 ) );
   _ASSERTE( ( bday->Month > 0 ) && ( bday->Month < 13 ) );
   _ASSERTE( ( bday->Year > 0 ) && ( bday->Year < 1996 ) );
}


/* ALLOCATION HOOK FUNCTION
   -------------------------
   An allocation hook function can have many, many different
   uses. This one simply logs each allocation operation in a file.
*/
int __cdecl MyAllocHook(
   int      nAllocType,
   void   * pvData,
   size_t   nSize,
   int      nBlockUse,
   long     lRequest,
   const unsigned char * szFileName,
   int      nLine
   )
{
   char *operation[] = { "", "allocating", "re-allocating", "freeing" };
   char *blockType[] = { "Free", "Normal", "CRT", "Ignore", "Client" };

   if ( nBlockUse == _CRT_BLOCK )   // Ignore internal C runtime library allocations
      return( TRUE );

   _ASSERT( ( nAllocType > 0 ) && ( nAllocType < 4 ) );
   _ASSERT( ( nBlockUse >= 0 ) && ( nBlockUse < 5 ) );

   fprintf( logFile,
            "Memory operation in %s, line %d: %s a %d-byte '%s' block (# %ld)\n",
            szFileName, nLine, operation[nAllocType], nSize,
            blockType[nBlockUse], lRequest );
   if ( pvData != NULL )
      fprintf( logFile, " at %X", pvData );

   return( TRUE );         // Allow the memory operation to proceed
}


/* REPORT HOOK FUNCTION
   --------------------
   Again, report hook functions can serve a very wide variety of purposes.
   This one logs error and assertion failure debug reports in the
   log file, along with 'Damage' reports about overwritten memory.

   By setting the retVal parameter to zero, we are instructing _CrtDbgReport
   to return zero, which causes execution to continue. If we want the function
   to start the debugger, we should have _CrtDbgReport return one.
*/
int MyReportHook(
   int   nRptType,
   char *szMsg,
   int  *retVal
   )
{
   char *RptTypes[] = { "Warning", "Error", "Assert" };

   if ( ( nRptType > 0 ) || ( strstr( szMsg, "DAMAGE" ) ) )
      fprintf( logFile, "%s: %s", RptTypes[nRptType], szMsg );

   retVal = 0;

   return( TRUE );         // Allow the report to be made as usual
  
}
#endif                     // End of #ifdef _DEBUG



/*****************************************************************
 *  MAIN FUNCTION                                                *
 *****************************************************************/
void main( )
{
   int i, j;

#ifdef _DEBUG
   _CrtMemState checkPt1;
   char timeStr[10], dateStr[10];         // Used to set up log file

   // Send all reports to STDOUT, since this example is a console app
   _CrtSetReportMode(_CRT_WARN, _CRTDBG_MODE_FILE);
   _CrtSetReportFile(_CRT_WARN, _CRTDBG_FILE_STDOUT);
   _CrtSetReportMode(_CRT_ERROR, _CRTDBG_MODE_FILE);
   _CrtSetReportFile(_CRT_ERROR, _CRTDBG_FILE_STDOUT);
   _CrtSetReportMode(_CRT_ASSERT, _CRTDBG_MODE_FILE);
   _CrtSetReportFile(_CRT_ASSERT, _CRTDBG_FILE_STDOUT);

   // Set the debug heap to report memory leaks when the process terminates,
   // and to keep freed blocks in the linked list.
   SET_CRT_DEBUG_FIELD( _CRTDBG_LEAK_CHECK_DF | _CRTDBG_DELAY_FREE_MEM_DF );

   // Open a log file for the hook functions to use
   logFile = fopen( "MEM-LOG.TXT", "w" );
   if ( logFile == NULL )
      FatalError( FILE_IO_ERROR );
   _strtime( timeStr );
   _strdate( dateStr );
   fprintf( logFile,
            "Memory Allocation Log File for Example Program, run at %s on %s.\n",
            timeStr, dateStr );
   fputs( lineStr, logFile );

   // Install the hook functions
   _CrtSetDumpClient( MyDumpClientHook );
   _CrtSetAllocHook( MyAllocHook );
   _CrtSetReportHook( MyReportHook );

#endif                     // End of #ifdef _DEBUG

   HeadPtr = NULL;

   // Create a trial birthday record.
   createRecord( 1749, 3, 23, "Pierre de Laplace" );

   // Check the debug heap, and dump the new birthday record. --Note that
   // debug C runtime library functions such as _CrtCheckMemory( ) and
   // _CrtMemDumpAllObjectsSince( ) automatically disappear in a release build.
   _CrtMemDumpAllObjectsSince( NULL );
   _CrtCheckMemory( );
   _CrtMemCheckpoint( &checkPt1 );

   // Since everything has worked so far, create more records
   for ( i = 0, j = 0; i < TEST_RECS; i++, j+=3 )
      createRecord( Dates[j], Dates[j+1], Dates[j+2], Names[i] );

   // Examine the results
   _CrtMemDumpAllObjectsSince( &checkPt1 );
   _CrtMemCheckpoint( &checkPt1 );
   _CrtMemDumpStatistics( &checkPt1 );
   _CrtCheckMemory( );

   // This fflush needs to be removed...
   fflush( logFile );

   // Now try displaying the records, which frees the memory being used
   printRecords( );

   // OK, time to go. Did I forget to turn out any lights? I could check
   // explicitly using _CrtDumpMemoryLeaks( ), but I have set
   // _CRTDBG_LEAK_CHECK_DF, so the C runtime library debug heap will
   // automatically alert me at exit of any memory leaks.

#ifdef _DEBUG
   fclose( logFile );
#endif
}

Output

Screen output:

Dumping objects ->
C:\DEV\EXAMPLE2.C(327) : {13} client block at 0x00661B38, subtype 0, 36 bytes long:
   The birthday of Pierre de Laplace is 3/23/1749.
Object dump complete.
Dumping objects ->
C:\DEV\EXAMPLE2.C(338) : {18} client block at 0x00661CB4, subtype 0, 36 bytes long:
   The birthday of Thomas Carlyle is 12/16/1770.
C:\DEV\EXAMPLE2.C(338) : {17} client block at 0x00661C68, subtype 0, 36 bytes long:
   The birthday of Ludwig van Beethoven is 12/4/1795.
C:\DEV\EXAMPLE2.C(338) : {16} client block at 0x00661C1C, subtype 0, 36 bytes long:
   The birthday of Carl Friedrich Gauss is 4/32/1777.
C:\DEV\EXAMPLE2.C(219) : Assertion failed: ( bday->Day > 0 ) && ( bday->Day < 32 )
C:\DEV\EXAMPLE2.C(338) : {15} client block at 0x00661BD0, subtype 0, 36 bytes long:
   The birthday of Thomas Jefferson is 4/13/1743.
C:\DEV\EXAMPLE2.C(338) : {14} client block at 0x00661B84, subtype 0, 36 bytes long:
   The birthday of George Washington is 2/11/1732.
Object dump complete.
0 bytes in 0 Free Blocks.
0 bytes in 0 Normal Blocks.
6442 bytes in 12 CRT Blocks.
0 bytes in 0 IgnoreClient Blocks.
216 bytes in 6 (null) Blocks.
Largest number used: 6658 bytes.
Total allocations: 6658 bytes.
memory check error at 0x00661C8C = 0x00, should be 0xFD.
DAMAGE: after (null) block (#17) at 0x00661C68.
(null) allocated at file C:\DEV\EXAMPLE2.C(338).
(null) located at 0x00661C68 is 36 bytes long.
memory check error at 0x00661C40 = 0x00, should be 0xFD.
DAMAGE: after (null) block (#16) at 0x00661C1C.
(null) allocated at file C:\DEV\EXAMPLE2.C(338).
(null) located at 0x00661C1C is 36 bytes long.
memory check error at 0x00661C40 = 0x00, should be 0xFD.
DAMAGE: after (null) block (#16) at 0x00661C1C.
memory check error at 0x00661C8C = 0x00, should be 0xFD.
DAMAGE: after (null) block (#17) at 0x00661C68.


This is the birthday list:
   Pierre de Laplace was born on March 23, 1749.
   George Washington was born on February 11, 1732.
   Thomas Jefferson was born on April 13, 1743.
   Carl Friedrich Gauss was born on April 32, 1777.
   Ludwig van Beethoven was born on December 4, 1795.
Detected memory leaks!
Dumping objects ->
C:\DEV\EXAMPLE2.C(338) : {18} client block at 0x00661CB4, subtype 0, 36 bytes long:
   The birthday of Thomas Carlyle is 12/16/1770.
Object dump complete.

Log file output:

Memory Allocation Log File for Example Program, run at 14:11:01 on 04/28/95.
-----------------------------------------------------------------------------
Memory operation in C:\DEV\EXAMPLE2.C, line 327:
                                   allocating a 36-byte 'Client' block (# 13)
Memory operation in C:\DEV\EXAMPLE2.C, line 338:
                                    allocating a 36-byte 'Client' block (# 14)
Memory operation in C:\DEV\EXAMPLE2.C, line 338:
                                    allocating a 36-byte 'Client' block (# 15)
Memory operation in C:\DEV\EXAMPLE2.C, line 338:
                                    allocating a 36-byte 'Client' block (# 16)
Memory operation in C:\DEV\EXAMPLE2.C, line 338:
                                    allocating a 36-byte 'Client' block (# 17)
Memory operation in C:\DEV\EXAMPLE2.C, line 338:
                                    allocating a 36-byte 'Client' block (# 18)
Assert: C:\DEV\EXAMPLE2.C(219) : Assertion failed:
                                    ( bday->Day > 0 ) && ( bday->Day < 32 )
Warning: DAMAGE: after (null) block (#17) at 0x00661C68.
Warning: DAMAGE: after (null) block (#16) at 0x00661C1C.
Memory operation in (null), line 0: freeing a 0-byte 'Client' block (# 0)
 at 661B38Memory operation in (null), line 0:
                                    freeing a 0-byte 'Client' block (# 0)
 at 661B84Memory operation in (null), line 0:
                                    freeing a 0-byte 'Client' block (# 0)
 at 661BD0Memory operation in (null), line 0:
                                    freeing a 0-byte 'Client' block (# 0)
 at 661C1CError: DAMAGE: after (null) block (#16) at 0x00661C1C.
Memory operation in (null), line 0: freeing a 0-byte 'Client' block (# 0)
 at 661C68Error: DAMAGE: after (null) block (#17) at 0x00661C68.

Debug Functions