SUBSYSTEM OVERVIEW
Heap Debugger (HDB) Subsystem
The HDB subsystem provides an application developer with tools to debug heap memory problems. The HDB subsystem works by replacing the standard heap functions malloc, calloc, realloc, free, and cfree with debugging versions. These debugging functions automatically check for the following common heap problems:
Allocations/reallocations of 0 bytes
Reallocations of non-allocated pointers
Freeing of non-allocated pointers
Freeing of NULL
Freeing of a previously freed pointer
Modification of previously freed memory
The functions can also be used to display a listing of memory allocated in the heap. This capability is useful in tracking down "memory leaks". A file can be optionally generated containing a trace of each heap function call along with the calling arguments. The HDB subsystem can also simulate an out of memory condition.
Several of the functions can be called interactively from a debugger to aid in tracking down heap problems.
The HDB subsystem is not compatible with system supplied heap debugging libraries and attempting to link with one may result in multiply defined functions.
COMPILING AND LINKING WITH HEAP DEBUGGING
To compile with heap debugging enabled you must link with the debug versions of the libraries. This will enable most of the debugging checks although you may wish to explicitly enable other checks by calling hdb_SetOption.
The heap debugger assumes that declarations of heap functions are performed before the first pdxlib include file is referenced (i.e. if malloc.h or stdlib.h is to be included in a function they must be referenced before any xxx_Interface.h files). This is required so that the macros redefining the heap functions do not redefine them in an external declaration statement. The macros can be disabled to compile some functions where this is not controllable (such as the output of yacc) by defining HDB_OFF before the first xxx_Interface.h include file. It may also be specified on the compile command line using the appropriate option. Memory allocated with functions compiled using HDB_OFF will have "Unknown" for the name of the allocating function.
REQUEST NUMBERS
Each call made to a heap function is assigned a request number. This number can be used to correlate output from the trace option to blocks of memory in the heap and for setting heap breakpoints. These numbers are consistent from one run to the next.
DEBUGGING OPTIONS
Due to the overhead incurred by some checks they are disabled by default and must be explicitly turned on. This is accomplished by calling hdb_SetOption with one of the following options:
Enables heap validation at each heap manager call. Any discrepancies in the heap will be displayed along with the file and line at which the heap was last validated successfully. Using this option will narrow down a problem to a location between the call which generated the last successful validation and the current heap call. This option does incur additional overhead as the heap must be traversed and validated at each heap call.
Memory which is deallocated via free or cfree will be checked for modification each time the heap is validated (i.e. at every call if the HDB_VALIDATE_HEAP option is on or whenever hdb_ValidateHeap is called). This option stores a checksum of the block and then computes a new checksum for the block each time the heap is validated. If the checksums differ an error is reported. This will catch errors in which a block of memory is modified after being freed. Note that by using this option the memory is never actually freed so that it can be checked for modifications. This can result in a large amount of heap being used. The function hdb_FlushFrees can be called to actually free these blocks of memory. Also note that this option will tremendously slow down execution if there are a lot of frees or calls to hdb_ValidateHeap. This overhead can be reduced by not specifying the HDB_VALIDATE_HEAP option. Also note that this option will not catch reads from blocks which have been freed (see HDB_ZERO_FREES). Only blocks freed while HDB_CHECK_FREES is enabled will be checked for modification.
Memory which is deallocated via free or cfree will be set to 0s before being returned to the heap. This option will catch some problems where code is referencing memory in a block after it has been freed. This may result in a behavior change (such as a SEGV) which can aide in tracking down the problem.
Generates a trace of heap function calls. The trace includes the function name, where it was called from, its arguments, and the return value (if any). By default the trace information is written to stdout (this can be changed as described in the REDIRECTING OUTPUT section). This option incurs some additional overhead.
Forces all allocation functions (malloc, calloc, and realloc) to behave as if no free space remained in the heap (i.e. a NULL will always be returned while this option is set).
There are no restrictions on combinations of options. If HDB_ZERO_FREES and HDB_CHECK_FREES are both specified a freed block of memory will be zeroed before its checksum is computed. Options can be turned off by calling hdb_ClearOption. Options can be selectively turned on around suspect portions of code to minimize the overhead (particularly useful for HDB_CHECK_FREES).
DISPLAYING HEAP INFORMATION
The contents of the heap can be displayed at any time by calling the function hdb_DumpHeap. This will produce a listing similar to the one below:
| Heap Contents | |||
| Rqst | Pointer | Size | Owner |
| – | |||
| 7 | 0x11cb8 | 256 | main.c(18) |
| 8 | 0x11de8 | 76 | add_line.c(19) |
| 9 | 0x11e60 | 32 | create_list.c(34) |
| 11 | 0x11f08 | 12 | olm__CreateBlock.c(56) |
| 12 | 0x11f40 | 120 | Unknown(0) |
| 13 | 0x11fe8 | 1000 | olm__CreateBlock.c(70) |
| 15 | 0x12400 | 9 | add_line.c(23) |
| 16 | 0x12438 | 256 | add_line.c(24) |
Heap Function Statistics
| mallocs: | 14 |
| callocs: | 0 |
| reallocs: | 0 |
| frees: | 7 |
| strdups: | 1 |
Total allocated memory 1761 bytes in 8 blocks Maximum allocated memory 2029 bytes in 14 blocks Maximum number of blocks 14
The first portion of the dump lists the allocated blocks in the heap when hdb_DumpHeap was called. Calling this function at the end of an application will list out all memory which was never freed. The first column in the listing represents the function request number in which the memory was allocated or reallocated. This number can be used to determine exactly where the memory was allocated (see HEAP BREAKPOINTS). The pointer column shows the starting address of the block of memory. Size indicates how many bytes of memory are allocated at the corresponding address. The owner column shows where the memory was allocated or reallocated at (on some non-ANSI C systems this column may contain mostly "UNKNOWN(0)", in this case the owner information is not available). If the owner information column contains "Unknown(0)" then the memory was allocated in a function compiled without heap debugging (either by specifying HDB_OFF or by not including a xxx_Interface.h file). Memory allocated by the operating system or other libraries will also be listed as "Unknown(0)".
The remainder of the information documents how many calls were made to each of the heap functions, the total current allocated memory and number of blocks, the maximum amount of memory allocated at any point and number of blocks, and the maximum number of blocks allocated at any point. This information can displayed at any time by calling hdb_DumpStats.
INTERACTIVE USE
Several of the functions are designed to be called interactively from a symbolic debugger (several dialects of dbx and some other debuggers support this capability). The functions which may be called interactively are:
hdb_SetOption hdb_ClearOption hdb_DumpHeap hdb_DumpPtr hdb_DumpStats hdb_ValidateHeap hdb_SetBreak hdb_FlushFrees
To specify option values to hdb_SetOption you must look up the appropriate #define in hdb_Interface and specify that value.
Each of the allocation functions malloc, calloc, and realloc assign a unique number to each allocated block of memory. Using this number it is possible to determine exactly where a given block of memory is allocated or freed. This is accomplished by setting a heap breakpoint using the function hdb_SetBreak which accepts a request number to break at. When the specified request is encountered, the function hdb_Break will be called with the file, line, and function at which the specified request was encountered. By placing a breakpoint in hdb_Break using a debugger, the call stack can be examined to see exactly where the request was made.
By placing a breakpoint in the hdb_Error function you can stop an application whenever a heap error is detected (such as from using the HDB_VALIDATE_HEAP option).
By using the hdb_SetBreak call and the HDB_NO_MEMORY option, out of memory conditions can be simulated at any point during the execution of an application.
It may be useful to define a set of aliases to access these functions (in debuggers which support this). The following examples are for dbx under SunOS:
alias dh "call hdb_DumpHeap()" alias ds "call hdb_DumpStats()" alias dp "call hdb_DumpPtr(!:1)" alias vh "call hdb_ValidateHeap()" alias shb "call hdb_SetBreak(!:1)" alias so "call hdb_SetOption(!:1)" alias co "call hdb_ClearOption(!:1)"
REDIRECTING OUTPUT
Normally the output from the heap debugging routines is sent to stderr (except trace output which is sent to stdout). This may be changed with the following functions:
- Set output of heap debugging functions
- Set destination of trace output
DEBUGGER ERROR MESSAGES
The following messages and their possible causes are listed below using the conventions:
- The heap function where the error occurred
- The source file and line which called func
- Request number of the call
Caused by overwriting the memory before the block or passing an invalid pointer to hdb_DumpPtr. The block in question is located at 0xnnnn.
Caused by overwriting the memory before the block or passing an invalid pointer to a heap function. The block in question is located at 0xnnnn.
A call was made to hdb_DumpPtr with a parameter of NULL.
A call was made to free with a parameter of NULL.
The free function was called more than once with the same pointer.
An allocation function was called with a size parameter of 0.
If nnn is a very large number then the size parameter to an allocation function may be invalid. Also generated if no more memory is available from the system heap. Calling hdb_FlushFrees is likely to help if the latter is true and the HDB_CHECK_FREE option is being used.
The realloc function was called with a pointer parameter of NULL.
The strdup function was called with a pointer parameter of NULL.
The memory before the block was overwritten.
The memory after the block was overwritten.
The contents of a freed block of memory was modified.
The last successful call to hdb_ValidateHeap was made at the specified file, line, and request number. The heap was corrupted somewhere between this call and the current one.