memory provides a debugging interface for malloc and free. It is designed to provide diagnostics without changing the functional behavior of malloc and free, and it may be omitted entirely from a project, without effect.

memory.h

Copyright © 2003, 2004 Dave Bayer. Subject to the terms and conditions of the MIT License.

MEMORYDEBUG

If MEMORYDEBUG is defined, then diagnostics are run on allocated memory, in hopes of detecting memory bugs.

#define MEMORYDEBUG1 1

memoryVerify

memoryVerify is defined as memoryVerifyDebug or memoryVerifyCheck, depending on whether or not MEMORYDEBUG is defined.

#if MEMORYDEBUG
#define memoryVerify memoryVerifyDebug
#else
#define memoryVerify memoryVerifyCheck
#endif

malloc

malloc is defined as mallocDebug or mallocCheck, depending on whether or not MEMORYDEBUG is defined.

#if MEMORYDEBUG
#define malloc(X) mallocDebug( (X), __FILE__, __LINE__, 0 )
#else
#define malloc mallocCheck
#endif

free

free is defined as freeDebug or freeCheck, depending on whether or not MEMORYDEBUG is defined. In either case, a handle is passed so the freed pointer can be set to null.

#if MEMORYDEBUG
#define free(X) freeDebug( (X), __FILE__, __LINE__ )
#else
#define free freeCheck
#endif

mallocCount

mallocCount reports to stdout the number of not yet freed, malloc allocations, if this count is nonzero. If MEMORYDEBUG is defined, then mallocCount also reveals an example of a not yet freed allocation.

#if MEMORYDEBUG
#define mallocCount mallocCountDebug
#else
#define mallocCount mallocCountCheck
#endif

Function prototypes:

#if MEMORYDEBUG

void memoryVerifyDebug( void );
void *mallocDebug( size_t size, char *fileName, int lineNo, int ours );
void freeDebug( void *p, char *fileName, int lineNo );
void mallocCountDebug( void );

#else

void memoryVerifyCheck( void );
void *mallocCheck( size_t size );
void freeCheck( void *p );
void mallocCountCheck( void );

#endif

memory.c

Copyright © 2003, 2004 Dave Bayer. Subject to the terms and conditions of the MIT License.

#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include "memory.h"

Undefine the malloc and free macros, so that code here can call the system versions.

#undef malloc
#undef free

mallocN

mallocN is used to count memory allocations, whether or not MEMORYDEBUG is defined.

static int mallocN = 0;

If MEMORYDEBUG is defined, then individually track all memory allocations.

#if MEMORYDEBUG

EVERYN

Check all of allocated memory for overwrites every EVERYN calls to malloc and free.

#define EVERYN 4096

PAD

Memory allocations are padded before and after with PAD bytes of memory, which should be a multiple of the byte alignment modulus. If bus errors persist with MEMORYDEBUG defined, try increasing the value for PAD.

#define PAD 32

FILL

FILL is the byte value used to clear the padding around memory allocations, and used to clear freed memory. A value other than 0 will detect a string terminator '\0' written into cleared memory.

#define FILL 1

memRecord

A memRecord records information about a memory allocation.

typedef struct memRecord
{
    char *fileName;
    int lineNo;
    size_t size;
    int freed, ours;
    int count;
    void *p;
    struct memRecord *next;
} memRecord;

#define memN 4093
static memRecord *memRecordPool, *memRecordArray[memN], spare, *memRecordSpare = &spare;

memRecordNew

memRecordNew initializes a new memRecord, and inserts it in memRecordArray.

static void memRecordNew( void *p, char *fileName, int lineNo, size_t size, int ours, int count )
{
    memRecord *r;
    int i;

We need a spare memRecord to cover the call to mallocDebug, to prevent an infinite recursion.

Changing 101 to 100 will cause a bus error if PAD is 16, but will produce an overwrite that gets caught if PAD is 32.

    if ( memRecordPool == 0 )
    {
        memRecordPool = memRecordSpare;
        r = mallocDebug( 101 * sizeof( *r ), __FILE__, __LINE__, 1 );
        for ( i=0; i<100; ++i )
        {
            r->next = memRecordPool;
            memRecordPool = r++;
        }
        memRecordSpare = r;
        r->next = 0;
    }

    r = memRecordPool;
    memRecordPool = r->next;

    i = ( (int) p ) % memN;
    r->next = memRecordArray[i];
    memRecordArray[i] = r;

    r->fileName = fileName;
    r->lineNo = lineNo;
    r->size = size;
    r->freed = 0;
    r->ours = ours;
    r->count = count;
    r->p = p;

    if ( count == 0 )
    {

By putting a debugging break here, one can examine a memory allocation that later causes an error.

        i = 0;
    }
}

clearMem

clearMem writes FILL to a memory range [from, to).

static void clearMem( void *p, int from, int to )
{
    char *s, *t;

    for ( s=t=p, s+=from, t+=to; s<t; ++s )
        *s = FILL;
}

checkMem

checkMem checks that a memory range [from, to) contains FILL, returning 1 on failure and 0 on success.

static int checkMem( void *p, int from, int to )
{
    char *s, *t;

    for ( s=t=p, s+=from, t+=to; s<t; ++s )
        if ( *s != FILL ) return 1;
    return 0;
}

abortMem

abortMem aborts execution after displaying an error message.

static void abortMem( char *msg, int count, char *fileName, int lineNo )
{
    fflush( stdout );
    fprintf( stderr, "\n%s: allocation %d, file \"%s\", line %d\nAborting\n", msg, count, fileName, lineNo );
    exit( 1 );
}

memoryVerifyDebug

memoryVerifyDebug is the debugging version of memoryVerify. It checks that no memory cleared with FILL has been overwritten.

void memoryVerifyDebug( void )
{
    memRecord *r, **pr;
    int i;

    for ( i=0, pr=memRecordArray; i<memN; ++i, ++pr )
        for ( r=*pr; r!=0; r=r->next )
        {
            if ( r->freed && checkMem( r->p, PAD, r->size + PAD ))
                abortMem( "memory written after free", r->count, r->fileName, r->lineNo );
            if ( checkMem( r->p, 0, PAD ) || checkMem( r->p, r->size + PAD, r->size + 2*PAD ))
                abortMem( "memory written outside bounds", r->count, r->fileName, r->lineNo );
        }
}

mallocDebug

mallocDebug is a debugging wrapper around malloc.

void *mallocDebug( size_t size, char *fileName, int lineNo, int ours )
{
    void *p;
    static int count = 0;

    if ( ++count % EVERYN == 0 ) memoryVerifyDebug();
    if ( !ours ) ++mallocN;
    p = malloc( size + 2*PAD );
    if ( p == 0 )
        abortMem( "malloc returned null", count, fileName, lineNo );
    memRecordNew( p, fileName, lineNo, size, ours, count );
    clearMem( p, 0, PAD );
    clearMem( p, size + PAD, size + 2*PAD );
    return p + PAD;
}

freeDebug

freeDebug is a debugging wrapper around free. Memory is cleared but not freed.

void freeDebug( void *p, char *fileName, int lineNo )
{
    memRecord *r;
    static int count = 0;
    int i;

    if ( ++count % EVERYN == 0 ) memoryVerifyDebug();
    --mallocN;
    p -= PAD;

    i = ( (int) p ) % memN;
    for ( r=memRecordArray[i]; r!=0; r=r->next )
        if ( r->p == p )
        {
            if ( r->freed )
                abortMem( "memory freed twice", r->count, fileName, lineNo );
            r->freed = 1;
            if ( checkMem( p, 0, PAD ) || checkMem( p, r->size + PAD, r->size + 2*PAD ))
                abortMem( "memory written outside bounds", r->count, r->fileName, r->lineNo );
            clearMem( p, PAD, r->size + PAD );
            return;
        }
    abortMem( "non-malloc memory freed", r->count, fileName, lineNo );
}

mallocCountDebug

mallocCountDebug reports to stdout the number of not yet freed, malloc allocations, if this count is nonzero, and reveals an example of a not yet freed allocation.

void mallocCountDebug( void )
{
    memRecord *r, **pr;
    int i;

    memoryVerifyDebug();
    if ( mallocN != 0 )
    {
        printf( "malloc allocations in use = %d\n", mallocN );
        for ( i=0, pr=memRecordArray; i<memN; ++i, ++pr )
            for ( r=*pr; r!=0; r=r->next )
                if ( !r->freed && !r->ours )
                {
                    printf( "\nmemory not freed: allocation %d, file \"%s\", line %d\n",
                        r->count, r->fileName, r->lineNo );
                    return;
                }
    }
}

If MEMORYDEBUG is not defined, then only carry out checks that do not significantly degrade execution time and space.

#else

memoryVerifyCheck

memoryVerifyCheck is the nondebugging version of memoryVerify. At present it does nothing.

void memoryVerifyCheck( void )
{
}

mallocCheck

mallocCheck is a wrapper around malloc.

void *mallocCheck( size_t size )
{
    ++mallocN;
    return malloc( size );
}

freeCheck

freeCheck is a wrapper around free.

void freeCheck( void *p )
{
    --mallocN;
    free( p );
}

mallocCountCheck

mallocCountCheck reports to stdout the number of not yet freed, malloc allocations, if this count is nonzero.

void mallocCountCheck( void )
{
    if ( mallocN != 0 )
        printf( "malloc allocations in use = %d\n", mallocN );
}

#endif