4

If an exception is thrown in a C++ program control is either transferred to the exception handler or terminate() is called.

Even if the program emits some diagnostics from inside the handler (or from terminate() handler) that can be too late - the most value is in the call stack at the point where the exception is thrown, not in the handler.

On Windows a call stack can be obtained using [StackWalk64()]1 function. The key is how to call that function at the right moment.

Is there a way to make a Visual C++ program execute some user code each time an exception (or an exception for which no handler is set) is thrown?

sharptooth
  • 167,383
  • 100
  • 513
  • 979

6 Answers6

3

If you want to do stuff when an SEH exception is thrown, such as when an access violation occurs, then you can simply catch the SEH exception (either with a __finally, or with a conversion to a C++ exception (see here)) and access the context within the exception which is the context at the time the exception was thrown. You can then generate either a callstack using StackWalker or a mini dump. IMHO it's better to produce a mini dump.

If you want to catch C++ exceptions at the point they're thrown and you don't have access to the source to the C++ exception classes then you need to get a bit craftier. I deal with this problem by running the target process under a custom debugger - use the Debug API (see here) which gets notifications of when an exception is thrown. At that point you can create a mini dump or call stack of the target process.

Community
  • 1
  • 1
Len Holgate
  • 21,282
  • 4
  • 45
  • 92
1

On Windows I'm using SetUnhandledExceptionFilter and MiniDumpWriteDump to produce a minidump.

icecrime
  • 74,451
  • 13
  • 99
  • 111
1

__try, __except are very helpful.

Goz
  • 61,365
  • 24
  • 124
  • 204
0

This is a great article on how to catch all different types of exceptions in Visual C++.
It also provides you with a crash dump that comes useful for debugging.

atoMerz
  • 7,534
  • 16
  • 61
  • 101
0

When the language doesn't support it, and you can't live without it, hack... :-/

#include <iostream>
#include <stdexcept>

namespace Throw_From
{
    struct Line
    {
        Line& set(int x) { x_ = x; return *this; }
        int x_;

        template <typename T>
        void operator=(const T& t) const
        {                                                                       
            throw t;
        }                                                                       
    };                                                                          
    Line line;                                                                  
}                                                                               

#define throw Throw_From::line.set(__LINE__) =                                  

void fn2()                                                                      
{                                                                               
    throw std::runtime_error("abc");                                            
}                                                                               

void fn1()                                                                      
{                                                                               
    fn2();                                                                      
}                                                                               

int main()                                                                      
{                                                                               
    try
    {
        fn1();
    }
    catch (const std::runtime_error& x)
    {
        std::cout << Throw_From::line.x_ << '\n';
    }
}
Tony Delroy
  • 102,968
  • 15
  • 177
  • 252
  • 1
    This won't work as written because `throw` has higher precedence than `,`; you end up throwing `Throw_From::line`, not `"ouch"`. – James McNellis Nov 19 '10 at 09:14
  • @James: yikes - good point... attempted workaround tenatively made... not looking good... any ideas? – Tony Delroy Nov 19 '10 at 09:19
  • 3
    Actually, this also has the bigger problem that you are not allowed to define a macro lexically identical to a keyword if you include any of the standard library headers, so `#define throw whatever` is out of the question. – James McNellis Nov 19 '10 at 09:24
  • 1
    @James: generally when people start thinking about calling platform specific stack dumps in exception situations, they don't care about portability or the Standard and will check their headers to see if it works. Lesser of evils and all that. (Hmmm... can't overload operator, - too late too) – Tony Delroy Nov 19 '10 at 09:31
  • 1
    `#define THROW(E) (do_stuff(), (throw E))` – Fred Nurk Nov 19 '10 at 09:34
  • 1
    @Tony: Those people compound a bad situation by making it worse with undetected UB. – Fred Nurk Nov 19 '10 at 09:36
  • @Fred: it's invasive though... every point of client usage has to change - if you were going to do that then you migth as well ditch the `#define` and have a nice function call (given you're getting the stack it doesn't matter about `__FILE__`, `__LINE__` - the usual excuses for `#define`). – Tony Delroy Nov 19 '10 at 09:38
  • @Fred: sometimes hackery is used as a temporary measure to find a specific bug... again, lesser of evils applies. – Tony Delroy Nov 19 '10 at 09:39
  • @Tony: It is invasive, but once you change the client code, you can transparently switch behavior by changing THROW. – Fred Nurk Nov 19 '10 at 09:40
  • @James, @Fred: I fully acknowledge the caveat about `#define` on keywords, but otherwise - the `operator=` overload looks promising...? :-) – Tony Delroy Nov 19 '10 at 09:47
  • 1
    @Tony, your solution is useful in cases you have the code, but it won't help if exception is thrown from 3rd party library. And it uses global static object Throw_From::line to store additional info, which is not thread safe. – Dialecticus Nov 26 '10 at 10:36
  • @Dialecticus: re sources - absolutely - just trying to avoid having to change source code, though you do need them and must recompile. That's the scope of this approach. Re global static - I wasn't meaning for that line thing to be taken so serious, as the real implementation involved calling a stack-trace function. For thread safety, you could use thread-specific buffer (if your compiler lacks it, lock & index a map by pthread_self() or OS's equivalent). All tunable given exact performance/functional requirements. – Tony Delroy Nov 26 '10 at 10:55
  • This soultion will not work if I can't recompile all the code. – sharptooth Nov 26 '10 at 10:55
  • @sharptooth: very true... if an issue, maybe you should add that to your requirements...? – Tony Delroy Nov 26 '10 at 10:57
0

Is there a way to make a Visual C++ program execute some user code each time an exception (or an exception for which no handler is set) is thrown?

Put that code into the constructor of your exception base class.

sbi
  • 219,715
  • 46
  • 258
  • 445
  • Good idea, but won't help if some unrelated exception is thrown. – sharptooth Nov 19 '10 at 09:19
  • @sharptooth: You can perform exception translation. It was always a good idea for me to wrap library exceptions (from Boost for example) in my own exception types. – Daniel Lidström Nov 26 '10 at 10:33
  • @Daniel Lidström: Yes, exception translation is great, but it's often useful to know where the original exception came from. – sharptooth Nov 26 '10 at 10:51