Here's a debugging war story for you on Linux with GDB.
There was this application which consumed 100% CPU right after startup, and forevermore. Classical infinite recursion, right? Well, for sure. But apart from taking 100% CPU, the rest of the classical telltale signs of infinite recursion were absent:
- No eventual crash due to stack space exhaustion
- No change in the backtrace when sampled at various points during runtime.
I finally sat down and single-stepped into the function that was consistently at the end of the backtrace. It was trivial, just calling the base class implementation and returning the value static_cast
ed, like this:
SomeOtherDerived * Derived::foo() const {
return static_cast<SomeOtherDerived*>( Base::foo() );
}
trivial... But when I did (single-step into the function) gdb would never return. It would hang there with 100% CPU, just like the application did without GDB attached...
I decided to take a good night's sleep over that one...
(if you want to venture a guess, stop reading here, spoiler below :)
When I came back next morning, I looked at the function body and the scales fell off my eyes. I had written
SomeOtherDerived * Derived::foo() const {
return static_cast<SomeOtherDerived*>( Derived::foo() );
}
That explained it: the function is trivial; esp. there are no stack variables (not even hidden ones), so there's no stack space used for each invocation. In addition, there's not even a new stack frame per call, which is why gdb got stuck when single-stepping.
(Of course, Derived
was named much more similarly to Base
than in this example).
Bottomline: experience is trump :)