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
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.
Just use new Throwable(). printStackTrace() method and it will print complete stack trace from where a method is called, into the console.
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.
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.
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!
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With