Search code examples
c++gccexceptionc++11code-injection

How can I print stack trace for caught exceptions in C++ & code injection in C++


I want to have stack trace not for my exceptions only but also for any descendants of std::exception

As I understand, stack trace is completely lost when exception is caught because of stack unwinding (unrolling).

So the only way I see to grab it is injection of code saving context info (stack trace) at the place of std::exception constructor call. Am I right?

If it is the case, please tell me how code injection can be done (if it can) in C++. Your method may be not completely safe because I need it for Debug version of my app only. May be I need to use assembler?

I'm interested only in solution for GCC. It can use c++0x features


Solution

  • Since you mentioned that you're happy with something that is GCC specific I've put together an example of a way you might do this. It's pure evil though, interposing on internals of the C++ support library. I'm not sure I'd want to use this in production code. Anyway:

    #include <iostream>
    #include <dlfcn.h>
    #include <execinfo.h>
    #include <typeinfo>
    #include <string>
    #include <memory>
    #include <cxxabi.h>
    #include <cstdlib>
    
    namespace {
      void * last_frames[20];
      size_t last_size;
      std::string exception_name;
    
      std::string demangle(const char *name) {
        int status;
        std::unique_ptr<char,void(*)(void*)> realname(abi::__cxa_demangle(name, 0, 0, &status), &std::free);
        return status ? "failed" : &*realname;
      }
    }
    
    extern "C" {
      void __cxa_throw(void *ex, void *info, void (*dest)(void *)) {
        exception_name = demangle(reinterpret_cast<const std::type_info*>(info)->name());
        last_size = backtrace(last_frames, sizeof last_frames/sizeof(void*));
    
        static void (*const rethrow)(void*,void*,void(*)(void*)) __attribute__ ((noreturn)) = (void (*)(void*,void*,void(*)(void*)))dlsym(RTLD_NEXT, "__cxa_throw");
        rethrow(ex,info,dest);
      }
    }
    
    void foo() {
      throw 0;
    }
    
    int main() {
      try {
        foo();
      }
      catch (...) {
        std::cerr << "Caught a: " << exception_name << std::endl;
        // print to stderr
        backtrace_symbols_fd(last_frames, last_size, 2);
      }
    }
    

    We basically steal calls to the internal implementation function that GCC uses for dispatching thrown exceptions. At that point we take a stack trace and save it in a global variable. Then when we come across that exception later on in our try/catch we can work with the stacktrace to print/save or whatever it is you want to do. We use dlsym() to find the real version of __cxa_throw.

    My example throws an int to prove that you can do this with literally any type, not just your own user defined exceptions.

    It uses the type_info to get the name of the type that was thrown and then demangles it.

    You could encapsulate the global variables that store the stacktrace a bit better if you wanted to.

    I compiled and tested this with:

    g++ -Wall -Wextra test.cc -g -O0 -rdynamic -ldl

    Which gave the following when run:

    ./a.out
    Caught a: int
    ./a.out(__cxa_throw+0x74)[0x80499be]
    ./a.out(main+0x0)[0x8049a61]
    ./a.out(main+0x10)[0x8049a71]
    /lib/i686/cmov/libc.so.6(__libc_start_main+0xe6)[0xb75c2ca6]
    ./a.out[0x80497e1]
    

    Please don't take this as an example of good advice though - it's an example of what you can do with a little bit of trickery and poking around at the internals!