65.9K
CodeProject is changing. Read more.
Home

Simulating Memory Allocation Failure for Unit Testing in C++

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.40/5 (10 votes)

Feb 17, 2004

4 min read

viewsIcon

56795

downloadIcon

370

How to test code against failure of operator new.

Introduction

The memory allocation and deallocation routines of C++, i.e. new and delete are used widely for allocating / deallocating memory from the free store. In general, the first line of main is written as:

int main()
{
    _set_new_handler ( MyHandler );
    /*
    .
    */
    return 0;
}

which is a good idea because you don't want your program to crash in case you run out of memory. But this has had its own drawback as it will arbitrarily transfer the control of the program to the function registered by calling _set_new_handler. This may not be the desirable case for each and every scenario. What you might be willing to do is to free some memory and try again.

To save the program from arbitrarily jumping from one place to another, many developers overload the new operator or catch the std::bad_alloc exception thrown from the new operator when it is unable to allocate the required memory and handle the non allocation of the memory.

Of the above two approaches, if you are using the second approach then you need to write the code in a way so that you can test your code against allocation failure, i.e. you handled all allocation failures in your code. I am giving here the way to simulate memory failures in your source code to test against non allocation of memory.

Handling Memory Allocation Failure

If you are using the second option then handling for memory allocation failure is necessary. This can be understood by looking at the code below. Suppose you overloaded you new operator to return NULL in case it fails to allocate the desired memory.

void    SimpleFunction ( )
{
    char    *Mem1    =    new char [ 32 ];
    strcpy ( Mem1 , "First Memory" );
}

If the new fails to allocate memory then it will return NULL and copying anything to NULL will lead to crash or core dump. So the above code must be written as:

void    SimpleFunction ( )
{
    char    *Mem1    =    new char [ 32 ];
    if ( Mem1 != NULL )
        strcpy ( Mem1 , "First Memory" );
}

That's safe enough, you are not going to copy anything to NULL. This is an immediate observation, but in a big code, you may easily forget to do so. So while doing your unit test, you must be able to check your code for such bugs. I will show you how to do it. You need to overload the new operator and write some debugging code along with your normal code.

Overloading the operator new

You need to overload the new operator in a way so that it returns NULL in case it fails to allocate the desired memory. The overloaded new operator will take an additional parameter which will direct it whether to allocate memory or simulate a failure by returning NULL. Here is the code that shows how to overload the new operator:

//    Overload the operator new
void *operator new ( size_t size , bool Alloc )
{
    if ( Alloc )
        return NULL;
    else
    {
        try
        {
            void *Memory    =    ::operator new ( size );
            return Memory;
        }
        catch ( std::bad_alloc )
        {
            return NULL;
        }
    }
    return NULL;
}

The second parameter to the operator is a boolean and the memory will be allocated by calling actual new only when this boolean is false. The code that calls actual new operator is encapsulated under try and catch block so that the exception std::bad_alloc can be handled to return NULL. Since we are overloading the new operator with an additional parameter, it leads to a problem. We must have placement delete corresponding to the new operator so that memory can be freed up if constructor throws an exception. If you won't provide placement delete nothing will happen. (MSVC6.0 gives a warning for this particular case). Here is the code containing the placement delete for the overloaded new operator:

//    Overload placement delete operator so that exceptions from
//    constructors can be handled
void operator delete ( void * Memory , bool Alloc )
{
    ::delete Memory;
}

Now, you have to write the code in a way so that you can simulate memory allocation failures in each function. We can run the same function multiple times to check the behavior of failed memory. The checking can be done in two ways.

  1. Simulate failure of memory one by one, i.e. first allocation gets failed in first pass, second allocation in second pass and so on. You need to remember how many allocations are there in the function.
  2. Simulate failure of all the memory that is inside that function in a single pass.

Code to simulate memory allocation failure

You can use a counter in the function that guides which particular memory allocation to fail. On the first pass, the value of counter will be zero and it will fail first memory allocation. On the second pass, the value of the counter will be one and it will fail the second memory allocation, and so on.

The function will also have a boolean variable that will simulate failure of all the allocation routines. Here is the code of SimpleFunction that shows how to simulate memory failure:

void    SimpleFunction ( int MemCounter , bool AllFail )
{

    int LocalCounter = 0;

    char *Mem1 = new ( ( MemCounter == LocalCounter ++ ) || AllFail ) char [ 32 ];
    if ( Mem1 != NULL )
        strcpy ( Mem1 , "First Memory" );

    char *Mem2 = new ( ( MemCounter == LocalCounter ++ ) || AllFail ) char [ 32 ];
    if ( Mem2 != NULL )
        strcpy ( Mem2 , "Second Memory" );

    int NumTimesLoop    =    10;
    int LoopCounter     =    0;
    
    //    Create a Variable that will be allocate Inner Memory
    char **InnerMemory = new ( ( MemCounter == LocalCounter ++ ) 
                             || AllFail ) char * [ NumTimesLoop ];
    
    //    Loop through and allocate the memory required    
    for ( LoopCounter = 0; LoopCounter < NumTimesLoop ; LoopCounter ++ )
    {
        InnerMemory [ LoopCounter ] = new ( ( MemCounter == LocalCounter ++ ) 
                                                   || AllFail ) char [ 32 ];
    }

}

The function takes two parameters. First parameter decides which allocation routines to fail and the second parameter if true will fail all the memory allocations. The only thing that remains is the test stub to call the function multiple times. This is written in the main code below:

int main()
{
    for ( int counter = 0; counter < 12; counter ++ )
        SimpleFunction ( counter , false );
    SimpleFunction ( -1 , true );

    return 0;
}

The main function for loop will call the function 12 times which will simulate the memory failure in each pass and the code written after the for loop will fail all memory allocations of the function. You can see that if anywhere in the function you forget to add suitable code for handling memory allocation failure, it will be reported by this method.

History

Last updated on Feb 15, 2004.