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:

HDB_VALIDATE_HEAP

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.

HDB_CHECK_FREES

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.

HDB_ZERO_FREES

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.

HDB_TRACE_FILE

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.

HDB_NO_MEMORY

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:

hdb_SetOutput

- Set output of heap debugging functions

hdb_SetTraceFile

- Set destination of trace output

DEBUGGER ERROR MESSAGES

The following messages and their possible causes are listed below using the conventions:

func

- The heap function where the error occurred

file(n)

- The source file and line which called func

rn

- Request number of the call

func: Corrupted block at 0xnnnn

Caused by overwriting the memory before the block or passing an invalid pointer to hdb_DumpPtr. The block in question is located at 0xnnnn.

func @file(n) Rqst rn: Corrupted block at 0xnnnn (or bad ptr)

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.

Can not dump NULL ptr

A call was made to hdb_DumpPtr with a parameter of NULL.

free @file(n) Rqst rn: Attempt to free NULL ptr

A call was made to free with a parameter of NULL.

free @file(n) Rqst rn: Pointer 0xnnnn already freed

The free function was called more than once with the same pointer.

func @file(n) Rqst rn: 0 bytes requested

An allocation function was called with a size parameter of 0.

func @file(n) Rqst rn: Could not allocate nnn bytes

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.

realloc @file(n) Rqst rn: Attempt to reallocate NULL ptr

The realloc function was called with a pointer parameter of NULL.

strdup @file(n) Rqst rn: NULL pointer passed

The strdup function was called with a pointer parameter of NULL.

func @file(n) Rqst rn: 0xnnn start marker bad

The memory before the block was overwritten.

func @file(n) Rqst rn: 0xnnn end marker bad

The memory after the block was overwritten.

func @file(n) Rqst rn: Freed block 0xnnnn modified

The contents of a freed block of memory was modified.

Last successful validation at file(n) Rqst: rn

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.