///C++ Exception-Handling Tricks for Linux

C++ Exception-Handling Tricks for Linux

Four Techniques for Dealing with Built-in Language Limitations

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.

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
#include

#include
#include

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.

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 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 g_objSegmentationFaultTranslator;

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

SignalTranslator g_objFloatingPointExceptionTranslator;

Managing Exceptions in Constructors and Destructors

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 .

•Purchase Linux books at discounted prices in the Linux section of the Developer Bookstore.

Order the no-charge 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.

2010-05-26T11:36:39+00:00 April 12th, 2005|Linux|0 Comments

About the Author:

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.

Leave A Comment