How to debug a program throwing an exception?
Posted on
Have you ever been confronted to a C++ exception that is hard to trace back? One of the problem with debugging C++ exceptions - at least with GDB - is that upon a throw, it directly jumps to the stack unwinding. Which is kind of confusing since the stack variables destroyed at that moment can be quite far from the throw instruction. And it leaves you without any information about the state of the stack at the moment of the throw. For instance, in the following toy example, an exception is thrown when a complex condition is met.
#include <exception> #include <string> #include <iostream> using std::exception; using std::string; using std::cerr; using std::endl; class MyException : public exception { string s; public: MyException(const string &_s) : exception(), s(_s) { } const char* what() const noexcept { return s.c_str(); } }; void f(int i) { static int j = 2; j *= (i%3 + 1); if(((i+j) & 0xFFF) == 2077) { // Complex condition throw MyException("Big error!"); } } int main() { for (int i = 1; i < 50; ++i) { f(i); } return 0; }
Running the debugger directly just causes an exit without any backtrace available. Besides, a step-by-step execution is very time-consuming because (Spoiler!) the throw happens at the 29th iteration! It would be cool to have GDB run and prompt only when the exception is thrown, wouldn't it? The easiest way to do so is by placing a breakpoint at the constructor of the exception class. For instance, you could place a breakpoint like the following:
break MyException::MyException( std::__cxx11::basic_string< char, std::char_traits
Or simply:
break MyException::MyException
Next time you run the program and it throws an instance of MyException, this simple trick will make GDB break and prompt you before the actual throw. This will allow you to examine the backtrace:
backtrace [full]
Then, going to "frame 1" will certainly be a very good starting point to further investigations to trace the root cause ;) Of course with the small example given here you could also set a breakpoint using the line number. But this technique does not scale up on big codebases, where an exception could originate from multiple locations. Those line-based breakpoints would be painful to set up. Alternatively, you could use a catchpoint:
catch throw
But this method also has its limits. It would prompt every time an exception is thrown, even if it is not the one you are interested in. Finally, if your C++ runtime (e.g. libstdc++) supports it, you could use the following catchpoint syntax to specify the exact type of exception you want GDB to stop upon:
catch throw MyException