Skip to main content

C++ exception-handling tricks for Linux

Four techniques for dealing with built-in language limitations

Sachin Agrawal (sachin_agrawal@in.ibm.com), Staff Software Engineer, IBM Software Labs, India
Sachin has been working extensively in C++ for more than six years, including three years of research into the C++ object models of various compilers. He currently works for IBM Global Services, India. You can contact him at sachin_agrawal@in.ibm.com.

Summary:  Handling exceptions in C++ has a few implicit restrictions at the language level, but you can get around them in some instances. Learn ways to make exceptions work for you so you can produce more reliable applications.

Date:  23 Feb 2005
Level:  Intermediate
Activity:  5689 views

Retaining exception source information

In C++, whenever an exception is caught within a handler, the information about the source of the exception is lost. The exact source of the exception could provide a lot of vital information to better handle it, or the information could be appended to the error log for postmortem.

To deal with this, you can generate a stack trace in the constructor of the exception object during the throw exception statement. ExceptionTracer is a class that demonstrates this behavior.


Listing 1. Generating a stack trace in the exception object constructor

 // Sample Program:
 // Compiler: gcc 3.2.3 20030502
 // Linux: Red Hat

 #include <execinfo.h>
 #include <signal.h>

 #include <exception>
 #include <iostream>

 using namespace std;

 /////////////////////////////////////////////

 class ExceptionTracer
 {
 public:
     ExceptionTracer()
     {
         void * array[25];
         int nSize = backtrace(array, 25);
         char ** symbols = backtrace_symbols(array, nSize);

         for (int i = 0; i < nSize; i++)
         {
             cout << symbols[i] << endl;
         }

         free(symbols);
     }
 };


Managing signals

Whenever a process performs an offending action such that the Linux™ kernel raises a signal, the signal must be handled. The signal handler generally releases the important resources and terminates the application. In this case, all the object instances on the stack are left un-destructed. On the other hand, if such signals are instead translated to C++ exceptions, you can gracefully invoke their destructors and program multiple levels of catch blocks to better deal with the signals.

However, you must remember the following:

  1. If the signal type is asynchronous or irrecoverable, this approach will not work. Not all the signal types are synchronous, meaning a different thread in that process may receive them. Also, a signal type may not be recoverable, and the process must shut down. To add to the complexity, a signal type on one kernel could be synchronous and on another kernel asynchronous. You can not write a single code that will work for all.

  2. Some Linux/UNIX systems require a stack fix-up before you can throw the C++ exception from within your signal handler. Again, the exact nature of stack fix-up depends on your kernel.

  3. In general, a signal that is translated to a Java Exception by any JVM on that platform can also be translated to a C++ exception.

SignalExceptionClass, defined in Listing 2, provides the abstraction of a C++ exception representing a signal that the kernel might rise. SignalTranslator is a template class based on SignalExceptionClass, which actually does the translation. There can be only one signal handler per signal per process active at any instant. Hence, SignalTranslator adopts a singleton design pattern. The whole concept is demonstrated using the SegmentationFault class for SIGSEGV and the FloatingPointException class for SIGFPE.


Listing 2. Translating signals to exceptions

 template <class SignalExceptionClass> class SignalTranslator
 {
 private:
     class SingleTonTranslator
     {
     public:
         SingleTonTranslator()
         {
             signal(SignalExceptionClass::GetSignalNumber(), SignalHandler);
         }

         static void SignalHandler(int)
         {
             throw SignalExceptionClass();
         }
     };

 public:
     SignalTranslator()
     {
         static SingleTonTranslator s_objTranslator;
     }
 };

 // An example for SIGSEGV
 class SegmentationFault : public ExceptionTracer, public exception
 {
 public:
     static int GetSignalNumber() {return SIGSEGV;}
 };

 SignalTranslator<SegmentationFault> g_objSegmentationFaultTranslator;

 // An example for SIGFPE
 class FloatingPointException : public ExceptionTracer, public exception
 {
 public:
     static int GetSignalNumber() {return SIGFPE;}
 };

 SignalTranslator<FloatingPointException> g_objFloatingPointExceptionTranslator;


Managing exceptions in constructors and destructors

Per ANSI C++, during construction and destruction of global (static global) variables, catching exceptions is not possible. Hence, ANSI C++ does not recommend throwing exceptions in the constructor and destructor of a class whose instances may be defined globally (static-globally). The other way to say it is, never define a global (static global) instance of a class whose constructor or destructor may throw exceptions. However, if you assume a specific compiler and a specific system, it may be doable, and fortunately, with GCC on Linux, it is.

This is demonstrated using the ExceptionHandler class, which again adopts a singleton design pattern. Its constructor registers an un-caught handler. Since there can be only one un-caught handler per process active at a time, the constructor should be invoked only once; hence, the reason for the singleton pattern. A global (static global) instance of ExceptionHandler should be defined prior to the definition of the actual global (static global) variable in question.


Listing 3. Handling exceptions in a constructor

 class ExceptionHandler
 {
 private:
     class SingleTonHandler
     {
     public:
         SingleTonHandler()
         {
             set_terminate(Handler);
         }

         static void Handler()
         {
             // Exception from construction/destruction of global variables
             try
             {
                 // re-throw
                 throw;
             }
             catch (SegmentationFault &)
             {
                 cout << "SegmentationFault" << endl;
             }
             catch (FloatingPointException &)
             {
                 cout << "FloatingPointException" << endl;
             }
             catch (...)
             {
                 cout << "Unknown Exception" << endl;
             }

             //if this is a thread performing some core activity
             abort();
             // else if this is a thread used to service requests
             // pthread_exit();
         }
     };

 public:
     ExceptionHandler()
     {
         static SingleTonHandler s_objHandler;
     }
 };

 //////////////////////////////////////////////////////////////////////////

 class A
 {
 public:
     A()
     {
         //int i = 0, j = 1/i;
         *(int *)0 = 0;
     }
 };

 // Before defining any global variable, we define a dummy instance
 // of ExceptionHandler object to make sure that
 // ExceptionHandler::SingleTonHandler::SingleTonHandler() is invoked
 ExceptionHandler g_objExceptionHandler;
 A g_a;

 //////////////////////////////////////////////////////////////////////////

 int main(int argc, char* argv[])
 {
     return 0;
 }


Handling exceptions in multi-threaded programs

Sometimes exceptions are left un-caught, which will cause the process to abort. Many times, however, processes contain multiple threads, where a few threads perform the core application logic while the rest service the external requests. If a service thread does not handle an exception due to programming error, it will kill the whole application. This may be undesirable as it promotes denial-of-service attacks by feeding illegal requests to the application. To avoid this, an un-caught handler can decide whether to invoke an abort or a thread-exit call. This is demonstrated towards the end of the ExceptionHandler::SingleTonHandler::Handler() function in Listing 3.


Conclusion

I've briefly discussed a few C++ programming design patterns to better perform the following tasks:

  • Tracing the source of exception while it is in process of being thrown.
  • Translating signals from the kernel to C++ exceptions.
  • Catching exceptions thrown during construction and/or destruction of global variables.
  • Exception handling in multithreaded processes.

I hope you're able to adopt some of these techniques to develop trouble-free code.


Resources

  • A classic text on C++ is The C++ Programming Language (Addison-Wesley, 1997) by Bjarne Stroustrup.

  • An excellent resource for understanding C++ internals is Inside the C++ Object Model (Addison-Wesley, 1996) by Stanley B. Lippman.

  • Don't forget also to check the system man pages for individual functions like backtrace(), backtrace_symbols(), signal(), abort(), and pthread_exit().

  • "Writing good exceptions" (developerWorks, May 2003) offers some practical advice on throwing and catching exceptions and refining your exception-handling sensibilities.

  • Java™ programmers might be interested to read "Best practices in EJB exception handling" (developerWorks, May 2002), which illustrates techniques for faster problem resolution.

  • Find more resources for Linux developers in the developerWorks Linux zone.

  • Get involved in the developerWorks community by participating in developerWorks blogs.

  • Browse for books on these and other technical topics.

  • Order the SEK for Linux, a two-DVD set containing the latest IBM trial software for Linux from DB2®, Lotus®, Rational®, Tivoli®, and WebSphere®.

  • Innovate your next Linux development project with IBM trial software, available for download directly from developerWorks.

About the author

Sachin Agrawal

Sachin has been working extensively in C++ for more than six years, including three years of research into the C++ object models of various compilers. He currently works for IBM Global Services, India. You can contact him at sachin_agrawal@in.ibm.com.

Comments (Undergoing maintenance)



Trademarks  |  My developerWorks terms and conditions

Help: Update or add to My dW interests

What's this?

This little timesaver lets you update your My developerWorks profile with just one click! The general subject of this content (AIX and UNIX, Information Management, Lotus, Rational, Tivoli, WebSphere, Java, Linux, Open source, SOA and Web services, Web development, or XML) will be added to the interests section of your profile, if it's not there already. You only need to be logged in to My developerWorks.

And what's the point of adding your interests to your profile? That's how you find other users with the same interests as yours, and see what they're reading and contributing to the community. Your interests also help us recommend relevant developerWorks content to you.

View your My developerWorks profile

Return from help

Help: Remove from My dW interests

What's this?

Removing this interest does not alter your profile, but rather removes this piece of content from a list of all content for which you've indicated interest. In a future enhancement to My developerWorks, you'll be able to see a record of that content.

View your My developerWorks profile

Return from help

static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=1
Zone=Linux
ArticleID=49678
ArticleTitle=C++ exception-handling tricks for Linux
publish-date=02232005
author1-email=sachin_agrawal@in.ibm.com
author1-email-cc=

My developerWorks community

Tags

Help
Use the search field to find all types of content in My developerWorks with that tag.

Use the slider bar to see more or fewer tags.

Popular tags shows the top tags for this particular content zone (for example, Java technology, Linux, WebSphere).

My tags shows your tags for this particular content zone (for example, Java technology, Linux, WebSphere).

Use the search field to find all types of content in My developerWorks with that tag. Popular tags shows the top tags for this particular content zone (for example, Java technology, Linux, WebSphere). My tags shows your tags for this particular content zone (for example, Java technology, Linux, WebSphere).

Special offers