Click here to Skip to main content
15,867,488 members
Articles / Programming Languages / C++
Article

Simulating Memory Allocation Failure for Unit Testing in C++

Rate me:
Please Sign up or sign in to vote.
4.40/5 (11 votes)
16 Feb 20044 min read 55.7K   370   18   3
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.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here


Written By
Architect
India India
A programmer by heart since 1998. Written code in C++, Java, JavaScript, Python & Ruby, Worked on Stack Development to Web Development. Data Specialist with SQL and NoSQL DBs

Comments and Discussions

 
GeneralDon't catch bad_alloc in operator new Pin
sdoyle17-Feb-04 3:21
sdoyle17-Feb-04 3:21 
Generalnew operator / operator new Pin
Craig Henderson17-Feb-04 1:02
Craig Henderson17-Feb-04 1:02 
GeneralRe: new operator / operator new Pin
S. Senthil Kumar17-Feb-04 4:58
S. Senthil Kumar17-Feb-04 4:58 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.