This thread has been locked.

If you have a related question, please click the "Ask a related question" button in the top right corner. The newly created question will be automatically linked to this question.

c++ new operator exception



Hi All,

Please could someone help me regarding the "new" operator exception in C6000 compiler?

 

I am trying to handle exception thrown by the new operator as I normally do in C++ programs.

My platform is 674x, CCS v.4.2.1. In the project I have my exceptions enabled.

To catch the problem I wrote a program simple program:

--------------------------------------------

#include <stdio.h>
int main(void)
{
    char* bufferPtr = 0;

    try
    {
        bufferPtr = new char[1000];
    }
    catch (...)
    {
        printf("Allocation exception has been thrown\n");
    }
   
    printf("Hello World!\n");


    delete[] bufferPtr;

    return 0;
}

-------------------------------------------- end of program

 

And my linker.cmd file is:

/*****************************************************************************

* linker command file for C6748 test code.
*
* © Copyright 2009, Logic Product Development, Inc. All Rights Reserved.
******************************************************************************/

-l rts67plus_eh.lib

-stack           0x00001000
-heap            0x00000200

MEMORY
{
   dsp_l2_ram:      ORIGIN = 0x11800000  LENGTH = 0x00040000
   shared_ram:      ORIGIN = 0x80000000  LENGTH = 0x00020000
   external_ram:    ORIGIN = 0xC0000000  LENGTH = 0x08000000
}

SECTIONS
{
   .text       > external_ram
   .const      > external_ram
   .bss        > external_ram
   .far        > external_ram
   .switch     > external_ram
   .stack      > external_ram
   .data       > external_ram
   .cinit      > external_ram
   .sysmem     > external_ram
   .cio        > external_ram
   .pinit      > external_ram
}

-------------------------------------------- end of linker.cmd

The size of the heap is set to throw exception when the new operator is used.

When the program is run on the "6747 Device Cycle Accurate Simulator, Little Endian" then excecution of the "new" operator causes the program to jump immediatelly to "C$$EXIT, abort:" label and just bypasses my exception handling.

Is it a bug or is it intended to jump. If so how can I control this scenario of invalid memory allocation?

 

Kind regards

Radek

  • Radoslaw Golebiewski said:
    excecution of the "new" operator causes the program to jump immediatelly to "C$EXIT, abort:" label and just bypasses my exception handling.

    Are you sure about that?  I notice your catch code contains a printf.  As described here, executing a printf requires a lot from the system, including a lot of memory.  After a failed new call, I wouldn't expect a printf to work.

    Thanks and regards,

    -George

  • Hi George,

    The printf function will not work in my example but after changing it to fprintf(stderr, ...) it should print.

    I changed my program and the problem seems to be random with the new operator. When I say random I mean in my bigger program it aborts on call of new operator (line 28 of "xmemory") but in my small program (see below) it does not. I investigated the case a little bit more and the problem is in string.reserve() function. If there is no sufficient memory on the heap the program shamefully aborts providing no information about the cause.

    The program below demonstrates this action:

    #include <stdio.h>
    #include <string>

    int main(void)
    {
        const size_t SIZE_TO_ALLOCATE = 20000;
        char* bufferPtr = 0;
       
        //The following block of code works as intended in this small program
        try
        {
            //These calls of the new operator cause bad_alloc exception
            //bufferPtr = new char[SIZE_TO_ALLOCATE];
            bufferPtr = (char *)::operator new(SIZE_TO_ALLOCATE);
            //bufferPtr = (char _FARQ *)::operator new(SIZE_TO_ALLOCATE);
            //(char _FARQ *)::operator new(SIZE_TO_ALLOCATE);
        }
        catch (std::bad_alloc& badAlloc)
        {
            fprintf(stderr, "Bad allocation exception caught (%s). This exception is expected...\n", badAlloc.what());
        }
        catch (...)
        {
            fprintf(stderr, "Unknown exception caught\n");
        }
       
        delete[] bufferPtr;
       
        //The following block of code DOES NOT WORK as expected
        //The program never reaches the bad_alloc exception catch
        try
        {
            std::string s;
            s.reserve(SIZE_TO_ALLOCATE);       
        }
        catch (std::bad_alloc& badAlloc)
        {
            fprintf(stderr, "Bad allocation exception caught (%s). This exception is never reached...\n", badAlloc.what());
        }
        catch (...)
        {
            fprintf(stderr, "Unknown exception caught\n");
        }
       
        printf("I wish I could get here...\n");
           
        return 0;
    }

    When stepping into the program aborts on line 28 in "xmemory" file:

    Line 28 of "xmemory": return ((_Ty _FARQ *)::operator new(_Count * sizeof (_Ty)));

    For me this seems to be a bug in the C6000 compiler but could someone else confirm my observations?

     

    Radek

     

  • The problem in the original example is that both exception handling and C I/O need to allocate heap space to perform their jobs, and there just isn't enough to do it.  You can see the problem a little more clearly with this cutdown test case:

    #include <stdio.h>
    
    int main(void)
    {
        try
        {
            throw int(0);
        }
        catch (...)
        {
            puts("Should have reached here");
        }
    
        puts("Hello World!");
    
        return 0;
    }
    

    If you look a PC trace of what's going on, the exception really is being thrown and caught as it should be, but the puts() in the catch block fails because there isn't enough space to allocate the output I/O buffer.  Basically, there isn't enough memory left to allow the program to tell you so; in that situation, C I/O is supposed to call abort().  (I must admit I'm a bit mystified as to why the second puts succeeds; it shouldn't.)

    If you add a call to setvbuf to prevent C I/O from allocating an output buffer, you'll see the text you expect:

    #include <stdio.h>
    
    char io_buffer[2];
    
    int main(void)
    {
        setvbuf(stdout, io_buffer, _IONBF, sizeof io_buffer);
    
        try
        {
            throw int(0);
        }
        catch (...)
        {
            puts("Should have reached here");
        }
    
        puts("Hello World!");
    
        return 0;
    }
    

     

     

  • Hello Archaeologist,

     

    Thank you for your last answer.

    I have added the following 5 lines to my simple program but is still does not catch exception at the std::string::reserve method.

    //Allocate the buffer to be used for I/O operations with the specified stream.

    char ioBufferStdOut[1000];
    setvbuf(stdout, ioBufferStdOut, _IONBF, sizeof(ioBufferStdOut));
    char ioBufferStdErr[1000];
    setvbuf(stderr, ioBufferStdErr, _IONBF, sizeof(ioBufferStdErr));

    I must catch this exception to make my program reliable!

    Reason: We are writting an application that once deployed will run for 15 years and will be responsible for road safety. In our development process we find much easier to create software using other IDEs so we write our application under Windows (VS) and Linux (gcc). We created the test environment with white-box testing aiming at 90% coverage. Having done about 50% of the application we started to port it to C6000. For the moment we want to port only our tests (they use EasyUnit framework) and see how (if at all) it works on C6000.

    The example that I gave in my first post is an excerption from our code where the test application goes astray (exactly on std::string::reserve()) and makes me very uncomfortable with C6000.

     

    Please, have you got any other ideas how could I catch the exception or how can I cause the program to throw an exception that I would be able to catch?

    Kind regards

    Radek

  • You want to verify the exception is being caught.  But you are having trouble doing that because the printf statement within the catch clause is causing problems.  Well, why focus on that at all?  Why not do something really simple like this ...

    int global_var = 0;
    int main(void)
    {
        char *bufferPtr = 0;
    
        try
        {
            bufferPtr = new char[1000];
        }
        catch (...)
        {
            global_var = 1;
        }
    
        delete[] bufferPtr;
        return 0;
    }
    

    Set a breakpoint on the "return 0;" statement in main, then see what the value of global_var is.  Then you'll know whether the catch clause executed or not.

    Thanks and regards,

    -George

  • The problem is that the heap is too small for exception handling to complete.  The internal exception handling functions in the RTS are not able to allocate enough memory to handle an exception thrown and caught during the execution of std::string::reserve, so exception handling calls std::terminate, which calls std::abort

    In helper functions called by std::string::reserve, there is a nested try-catch, like so:

    try 
    {                                                               
       _Ptr = allocate(roundedup(newsize));                  
    }
    catch (...)
    {                                                     
       try 
       {
          _Ptr = allocate(newsize);               
       } 
       catch (...) 
       {                                                 
          throw; // give up                                                     
       }                                                               
    }                                                                   
    

    exception handling is able to allocate enough memory to handle the first exception (thrown in the first try block), but it cannot free that memory until the first catch block has exited.  With a heap of only 0x200 bytes, almost all of the heap is used up at this point, and exception handling is unable to allocate the second exception.

    You can get this example to work by making the heap a little larger; 0x3c0 would suffice (assuming setvbuf is used to avoid C I/O buffer heap allocation).

    Exception handling and C I/O both require a decently-sized heap, and 0x200 is very small for C6000.

    You might also consider setting a custom std::terminate handler to make it more obvious when the program is terminating intentionally:

    #include <exception>
    
    void my_terminate(void)
    {
        puts("Hey, someone called std::terminate()");
    }
    
    int main(void)
    {
        std::set_terminate(my_terminate);
        [..]
    }
  • Hi Archaeologist,

    Thank you for your hint. I finally solved my problem an this is how I did it:

    1) I recompiled the library with -g option (enable debugging) and now I could see what is happening under the hood.

    For each Code Generation Tools there is a lib directory. This library contains the file rtssrc.zip. It is great that TI have provided the sources of their library because that makes life much easier. Having recompiled this library with enabled debugging I could trace exactly where the problem is.

    2) I changed the file xstring from the tool include path so that:


        void _Copy(size_type _Newsize, size_type _Oldlen)
            {    // copy _Oldlen elements to newly allocated buffer
            size_type _Newres = _Newsize | _ALLOC_MASK;
            if (max_size() < _Newres)
                _Newres = _Newsize;    // undo roundup if too big
            else if (_Newres / 3 < _Myres / 2
                && _Myres <= max_size() - _Myres / 2)
                _Newres = _Myres + _Myres / 2;    // grow exponentially if possible
            _Elem *_Ptr;

            _TRY_BEGIN
                _Ptr = _Mybase::_Alval.allocate(_Newres + 1);
            _CATCH_ALL
                _Newres = _Newsize;    // allocation failed, undo roundup and retry
                _TRY_BEGIN
                    _Ptr = _Mybase::_Alval.allocate(_Newres + 1);
                _CATCH_ALL
                _Tidy(true);    // failed again, discard storage and reraise
                _RERAISE;
                _CATCH_END
            _CATCH_END

            if (0 < _Oldlen)
                _Traits::copy(_Ptr, _Myptr(), _Oldlen);    // copy existing elements
            _Tidy(true);
            _Bx._Ptr = _Ptr;
            _Myres = _Newres;
            _Eos(_Oldlen);
            }

     

    Looks like:


        void _Copy(size_type _Newsize, size_type _Oldlen)
            {    // copy _Oldlen elements to newly allocated buffer
            size_type _Newres = _Newsize | _ALLOC_MASK;
            if (max_size() < _Newres)
                _Newres = _Newsize;    // undo roundup if too big
            else if (_Newres / 3 < _Myres / 2
                && _Myres <= max_size() - _Myres / 2)
                _Newres = _Myres + _Myres / 2;    // grow exponentially if possible
    //        _Elem *_Ptr;

            _Elem * _Ptr = _Mybase::_Alval.allocate(_Newres + 1);
    //        _TRY_BEGIN
    //            _Ptr = _Mybase::_Alval.allocate(_Newres + 1);
    //        _CATCH_ALL
    //            _Newres = _Newsize;    // allocation failed, undo roundup and retry
    //            _TRY_BEGIN
    //                _Ptr = _Mybase::_Alval.allocate(_Newres + 1);
    //            _CATCH_ALL
    //            _Tidy(true);    // failed again, discard storage and reraise
    //            _RERAISE;
    //            _CATCH_END
    //        _CATCH_END

            if (0 < _Oldlen)
                _Traits::copy(_Ptr, _Myptr(), _Oldlen);    // copy existing elements
            _Tidy(true);
            _Bx._Ptr = _Ptr;
            _Myres = _Newres;
            _Eos(_Oldlen);
            }

    This way in the case of being about the end of memory I still have the chance to catch exception and there is sufficient space to create this exception and catch it in the main program loop.

    I hope this solution would help others in the case of being short of DSP memory.

    Regards

  • Hi Radoslaw

     

    Our company is successfully developing a large c++ application that is compiled for x86, arm and C6x using Linux as  the  host OS. Just a heads up that if you are using Dspbios and threads as we are, then this combination is not supported. See  http://e2e.ti.com/support/development_tools/compiler/f/343/t/59236.aspx, and the expressdsp wiki.

     

    Cheers

    Brett