Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

like image 667
boqapt Avatar asked Jul 26 '12 09:07

boqapt


People also ask

How do I print a stack trace for exceptions?

Using printStackTrace() method − It print the name of the exception, description and complete stack trace including the line where exception occurred. Using toString() method − It prints the name and description of the exception. Using getMessage() method − Mostly used. It prints the description of the exception.

How do I print a stack trace?

Just use new Throwable(). printStackTrace() method and it will print complete stack trace from where a method is called, into the console.

How do I find stack trace error?

Use the console. trace() method to get the stack trace from an error. The console. trace() method outputs the stack trace and shows the call path taken to reach the point at which the method was called.

What is exception stack trace?

In simple terms, a stack trace is a list of the method calls that the application was in the middle of when an Exception was thrown. Simple Example. With the example given in the question, we can determine exactly where the exception was thrown in the application.


2 Answers

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!

like image 153
Flexo Avatar answered Sep 21 '22 13:09

Flexo


On Linux this can be implemented by adding a call to backtrace() in the exception constructor to capture the stack trace into an exception's member variable. Unfortunately, it won't work for standard exceptions, only for the ones you define.

like image 37
Maxim Egorushkin Avatar answered Sep 18 '22 13:09

Maxim Egorushkin