Why you cannot have C++ exceptions with a stack trace
di.unipi.it
di.unipi.it
Some ffis do this to present C++ stack traces to their host languages.
If variables/frames are optimized out, then yes, you can't display their contents. But anyone that has used gdb already knows this...
I work with projects where stack traces are added automatically to exceptions during throw. It works quite well, and in fact I would disagree with author on some of his points:
- stack trace not representative of source code, due to optimizations - unwinding information is consistent with what you have in a source code, and compiler optimization don't change that (i.e., it would be a compiler bug if it did, see any design document about debugging information, for example in LLVM / Clang).
- throw expression is not a place where the problem is - that is of course true in general, but how much more useful is stack trace indicating code path where exception have been thrown, than merely a type of exception. Name of function which have thrown an exception is also a good start, but of limited value for some common utility functions.
- memory allocation - dynamic memory allocation could be problematic, but storing stack trace in exception object itself is quite simple and avoid this problem altogether.
Because modern optimizations make that impossible. (How do you represent a statement that GVN eliminated, for example?) Not doing those optimizations would make code unacceptably slow.
(and with "structured exception handling" and a bit of hacking, you can get backtraces for machine exceptions as well)
Just use Google Breakpad. For Objective-C, you'll probably do better with PLCrashReporter (or possibly KSCrash) which can reliably unwind at the time of the crash because of certain assumptions it can make about ObjC binaries that the more general Breakpad does not make. (Breakpad just captures the stacks to a minidump, which you then unwind later using symbol files generated from your binaries.)
you usually need to allocate a piece of memory in the heap
Breakpad preallocates. The ObjC (Mac, iOS) handler included with Breakpad (should you go that route) goes so far as to mprotect the preallocated memory to help protect it from memory smashers.
None of this is perfect, but I'd rather have stack traces than not. It's also useful to have a breadcrumb mechanism in the app to include with the stack trace.
I also wanted stack traces in exceptions (or being able to get them at any time, really) and I wanted to make them encrypted, to avoid giving away the internal structure of our game and engine code.
The solution I came up with was to have a global array representing the stack trace, a macro that would create a small scoped object which pushed a new "stack trace" entry on creation and popped it on destruction, and a script that would add a call to this macro to the beginning of each method with any payload I wanted -usually the encrypted filename, line and signature of the method, but it was possible to append e.g. parameter values as well.
Since these strings were compile time constants, this whole thing was surprisingly cheap, and worked great for over a decade.
With C++ there always is a way :)
I don't think either of these are true. Some libraries make liberal use of exceptions, as a design decision. If your text editor tries to open an enormous file, you don't want it to crash, you want it to deallocate all of the memory it consumed for that file and then fall back with an error message.
I don't think the point about inlining is reasonable either: I wouldn't expect a fully optimised build to give me a full stack trace, that is what debug builds are for.
>If the exception is meant to signal the fact that the memory is finished, where are we allocating the stack trace?
If this is really a concern, allocate some memory at the start of execution that you can use in the event of a crash.
The naive texteditor should just check the size of the file before loading it into memory instead of trying to recover from a crash.
Getting stack traces for other exceptions IS useful - our code is full of assertions that throw when invariant assumptions are violated, and knowing the stack trace is useful for tracking down what system is misbehaving.
It's not perfect because all the destructors for your stack allocated objects have run. So for example, any std::string on the stack will be gone, but any pointers to structures on the heap are still intact.
In addition, just knowing what the stack was is invaluable in debugging even if you don't have the values of every variable.
It's a huge pain that you can't get that under linux.
Then you can do interesting things like print a stack trace at a "throw" point if you suspect a problem, or capture a trace into an exception base class in debug modes for printing at catch points, or even as a form of non-crashing assertion to log traces whenever unexpected conditions arise.
In GC'd language, implementing exceptions by simple non-local exit to catch/finally block is perfectly viable implementation strategy. It does not work so well in C++, where are implicit finally blocks that call destructors of local variables almost everywhere. To some extent this feature could be abused to produce stack-trace during the unwinding (but that necessarily involves injecting code to all functions in the program).
On the other hand, CL/Smalltalk-style condition systems sidestep this issue completely, by running the handler code before unwinding the stack. Windows' SEH allows you to do the same thing. And IIRC even in plain C++, handler for completely unhandled exception (that by default involves call to abort()) runs without stack unwinding (thus something along the lines of std::set_terminate (__gnu_cxx::__verbose_terminate_handler) gives you stacktraces for unhandled exceptions).