Ever wondered how to get information what was actually caught you enter catch(...) clause ?
With g++ (on some popular platforms) it's possible retrieve pointer to type_info record of base exception object type and what more important, pointer to exception object itself.
[Note, all information comes from http://www.codesourcery.com/public/cxx-abi/abi-eh.html]
Let's go ...
First, of all g++ runtime stores exception handling state in some structure that is different for each thread:
struct __cxa_eh_globals { __cxa_exception * caughtExceptions; unsigned int uncaughtExceptions; };
You can obtain pointer to instance via
extern "C" __cxa_eh_globals* __cxa_get_globals();call.
Next, when you got this you can look at first exception that you have - it's the one currently handled (i assume you're still in catch(...) clause). The __cxa_exception structure is quite almost obvious:
struct __cxa_exception { std::type_info * exceptionType; void (*exceptionDestructor) (void *); void* unexpectedHandler; void* terminateHandler; __cxa_exception * nextException; int handlerCount; int handlerSwitchValue; const char * actionRecord; const char * languageSpecificData; void * catchTemp; void * adjustedPtr; void* unwindHeader; };
Here most important member are:
- adjustedPtr
- pointer to exception object
- exceptionType
- type_info of exception object
Having that we can retrieve information for any type of exception object we know. std::exception - just call what(), std::string (yeah who throws them) - just print it, CORBA::exception, all messy Xerces exceptions ... whoa!
Sample proof of concept. Tested on following configurations
Meat:
#include <map> #include <string> #include <typeinfo> #include <iostream> #include <sstream> // catch clause analysis struct __cxa_exception { std::type_info * exceptionType; void (*exceptionDestructor) (void *); void* unexpectedHandler; void* terminateHandler; __cxa_exception * nextException; int handlerCount; int handlerSwitchValue; const char * actionRecord; const char * languageSpecificData; void * catchTemp; void * adjustedPtr; void* unwindHeader; }; struct __cxa_eh_globals { __cxa_exception * caughtExceptions; unsigned int uncaughtExceptions; }; extern "C" __cxa_eh_globals* __cxa_get_globals(); // yet another rtti like attempt struct extended_type_info { virtual const char* type_name() = 0; virtual std::string to_string(void*) = 0; virtual ~extended_type_info() {} }; typedef std::map<std::string, extended_type_info*> handler_map; template <typename Callable> void execute(Callable c, handler_map const& exc_handlers) { try { c(); } catch( ... ) { __cxa_eh_globals* gp = __cxa_get_globals(); if( gp == 0 && gp->caughtExceptions == 0 ) { std::cerr << "unknown exception caught (unable to find exception record)" << "\n"; } else { __cxa_exception* cp = gp->caughtExceptions; std::type_info* t = cp->exceptionType; void* exc_obj_ptr = cp->adjustedPtr; std::string exc_name(t->name()); handler_map::const_iterator ieh = exc_handlers.find(exc_name); if( ieh != exc_handlers.end() ) { extended_type_info* ti = ieh->second; std::cerr << "exception caught " << ti->type_name() << "(" << ti->to_string(exc_obj_ptr) << ")\n"; } else { std::cerr << "exception caught: " << t->name() << ", no handler found\n"; } } } } struct int_extended_type_info: public extended_type_info { const char* type_name() { return "int"; } std::string to_string(void* p) { int* v = reinterpret_cast<int*>(p); std::ostringstream ss; ss << *v; return ss.str(); } }; struct std_string_extended_type_info: public extended_type_info { const char* type_name() { return "std::string"; } std::string to_string(void* p) { std::string* v = reinterpret_cast<std::string*>(p); return *v; } }; template <typename T> class singletonof { public: static T* get_ptr() { return &instance; } private: static T instance; }; template <typename T> T singletonof<T>::instance; // the code void akuku() { throw 321; } void akuku_string() { throw std::string("akuku"); } int main(int argc, char** argv) { handler_map hm; hm["i"] = singletonof<int_extended_type_info>::get_ptr(); hm["Ss"] = singletonof<std_string_extended_type_info>::get_ptr(); execute(&akuku, hm); execute(&akuku_string, hm); }And the output is:
$ ./execution_monitor.exe exception caught int(321) exception caught std::string(akuku)Cool!
Update 1 [2009-03-06]: typo(sstream), added info about tested platforms
Update 2 [2009-03-09]: tested also on gcc/solaris 9
1 comment:
Thanks for the code! I grabbed it, adapted it slightly for my use, and compiled away; it worked like a charm.
Post a Comment