views:

317

answers:

6

Someone in a different question suggested using catch(...) to capture all otherwise unhandled - unexpected/unforseen exceptions by surrounding the whole main() with the try{}catch(...){} block.

It sounds like an interesting idea that could save a lot of time debugging the program and leave at least a hint of what happened.

The essence of the question is what information can be recovered that way (other than whatever debug globals I leave behind), and how to recover it (how to access and recognize whatever catch was called with)

Also, what caveats are connected with it. In particular:

  • will it play nice with threads that sprout later?
  • will it not break handling segfaults (captured elsewhere as signal)
  • will it not affect other try...catch blocks inevitably nested inside, that are there to handle expected exceptions?
+2  A: 

A global try catch block is useful for production systems, in order to avoid displaying a nasty message to the user. During development I believe that are best avoided.

Regarding your questions:

  • I believe that a global catch block won't catch exceptions in another thread. Each thread has its own stack space.
  • I am not sure about this.
  • Nested try...catch blocks aren't affected and will execute as usual. An exception propagates up the stack, until it finds a try block.
kgiannakakis
If by "avoid displaying a nasty message" you mean "replacing the nasty message with a readable one", then I agree. If you just mean removing the error message then that just leaves the user befuddled.
Tor Valamo
This is what I mean, to display a readable message to the user and not a decrypted stack trace.
kgiannakakis
On most systems if an exception escapes a thread entry point then the application unceremoniously terminates. Causing the app to stop dead without unwinding the main thread stack. But read your thread documentation carefully for details. But generally best to catch all exceptions at the threads base.
Martin York
+1  A: 

You could try a solution I use if you're making a .net application. That captures all unhandled exceptions. I generally only enable the code (with #ifndef DEBUG) for production code when I'm not using the debugger.

It's worth pointing out as kgiannakakis mentions that you can't capture exceptions in other threads, but you can use the same try-catch scheme in those threads and post the exceptions back to the main thread where you can re-throw them to get a full stack track of what went wrong.

Jon Cage
+1  A: 

and how to recover it (how to access and recognize whatever catch was called with)

If you mean how to recover the type of exception that was thrown, you can chain catch blocks for specific types (proceeding from more specific to more general) before falling back to catch (...):

try {
   ...
} catch (const SomeCustomException& e) {
   ...
} catch (const std::bad_alloc& e) {
   ...
} catch (const std::runtime_error& e) {
   // Show some diagnosic for generic runtime errors...
} catch (const std::exception& e) {
   // Show some diagnosic for any other unhandled std::exceptions...
} catch (...) {
   // Fallback for unknown errors.
   // Possibly rethrow or omit this if you think the OS can do something with it.
}

Note that if you find yourself doing this in multiple places and want to consolidate code (maybe multiple main functions for separate programs), you can write a function:

void MyExceptionHandler() {
   try {
      throw; // Rethrow the last exception.
   } catch (const SomeCustomException& e) {
      ...
   }
   ...
}

int main(int argc, char** argv) {
   try {
      ...
   } catch (...) {
      MyExceptionHandler();
   }
}
jamesdlin
Now that you've caught an unknown exception, what do you intend to do with it?
Piskvor
@Piskvor: If you've exhausted all the types of exceptions you know (or care) about, then there's nothing to do but to show an "Unknown internal error" message and terminate.
jamesdlin
@jamesdlin: ...which would happen anyway without the try block, so why bother?
Piskvor
@Piskvor: I suppose, but an application could still provide a friendlier error message than the default, which is likely to be full of jargon. It could even include support instructions.
jamesdlin
@Piskvor, @jamesdlin: ...or return the current task index of the task pump, event name if it was inside an event handler, or any of a hundred of suspect state variables I would not have the opportunity to access with the program quitting. Of course they are not reliable concerning containing correct data, but they are much better than nothing.
SF.
BAD Idea to catch and use EXIT_FAILURE. Some OS provide extra facilities for debugging exceptions that escape main(). catch and re-throw. Not any exception that has propagated this far has no reasonable potential for being corrected (if it did it would have been corrected before reaching here).
Martin York
@Martin: It's not meant for correction/recovery but for diagnostics. Fair enough point about EXIT_FAILURE; I've removed that part.
jamesdlin
@SF: you've already caught anything that inherits from std::exception; if you catch `...`, the program is in serious trouble and you may not be able to access your variables, let alone return or output them somewhere
Piskvor
@Piskvor: I may or may not be able. If I don't catch, then definitely I am not able. Unreliable is better than none.
SF.
A: 

A catch-all will not be terribly useful since there is no type/object information that you can query. However, if you can make sure all exceptions raised by your application are derived from a single base object, you can use a catch block for the base exception. But then that wouldn't be a catch-all.

dirkgently
@dirkgently Sorry, I completely misread your answer - I'll delete the comment.
anon
+3  A: 

As far as I remember, catch(...) on Win32 catches also SEH exceptions, and you do not want to do that. If you get a SEH exception it's because something very scary happened (mainly access violations), so you can't trust your environment anymore. Almost everything you could do may fail with another SEH exception, so it's not even worth trying. Moreover, some SEH exceptions are intended to be caught by the system; more on this here.

So, my advice is to use a base exception class (e.g. std::exception) for all your exceptions, and catch just that type in the "catchall"; your code cannot be prepared to deal with other kind of exceptions, since they are unknown by definition.

Matteo Italia
What if I end my catch block with `throw;` ?Anyway, when SEH occurs, there's not much worse that can happen other than entering SEH recursively (and then watchdog will kill me).
SF.
Even if you rethrew the exception, still your code would handle as exceptions some normal situations (e.g. access violation on stack guard pages, that is handled by the system automatically expanding the stack).If you generated a SEH exception in your exception handler it wouldn't be caught by your catchall (for that you would need to set up a global SEH handler), instead your app would simply crash; still, this would make useless the minidump, since all SEH exceptions would track back to the catchall instead of the real problematic code.
Matteo Italia
I'll just get this as optional debug facility then. Switching it off normally, on if a non-segfault exception causes problems.
SF.
Whether or not catch(...) catches SEH exceptions under Windows is compiler specific.For the Microsoft compilers, vc7's catch(...) always catches SEH exceptions. For vc8 onwards, there is a compiler option that enables that behaviour (`/EHa`), but it is off by default.
Joe Gauterin
Interesting, I didn't know about it (and actually I still use 7.1, so I knew just its behavior).
Matteo Italia
+4  A: 

Yes it is a good idea.

If you let an exception escape main it is implementation defined weather the stack is unwound before the application is shut down. So in my opinion it is essential you catch all exceptions in main.

The question then becomes what to do with them.
Some OS (See MS and SE) provide some extra debugging facilities so it is useful to just re-throw the exception after you catch it (because the stack has been unwound now anyway).

int main()
{
    try
    {
        /// All real code
    }
    // I see little point in catching other exceptions at this point 
    // (apart from better logging maybe). If the exception could have been caught
    // and fixed you should have done it before here.

    catch(std::exception const& e)
    {
         // Log e.what() Slightly better error message than ...
         throw;
    }
    catch(...)   // Catch all exceptions. Force the stack to unwind correctly.
    {
        // You may want to log something it seems polite.
        throw;  // Re-throw the exception so OS gives you a debug opportunity.
    }
}
  • will it play nice with threads that sprout later?

It should have no affect on threads. Usually you have to manually join any child threads to make sure that they have exited. The exact details of what happens to child threads when main exits is not well defined (so read your documentation) but usually all child threads will die instantly (a nasty and horrible death that does not involve unwinding their stacks).

If you are talking about exceptions in child threads. Again this is not well defined (so read your documentation) but if a thread exits via an exception (ie the function used to start the thread exits because of an exception and not a return) then this usually causes the application to terminate (same affect as above). So it is always best to stop ALL exceptions from exiting a thread.

  • will it not break handling segfaults (captured elsewhere as signal)

Signals are not affected by the exception handling mechanism.
But because signal handlers may place an odd structure on the stack (for their own return handling back to normal code) it is not a good idea to throw an exception from within a signal handler as this may cause unexpected results (and is definitely not portable).

  • will it not affect other try...catch blocks inevitably nested inside, that are there to handle expected exceptions?

Should have no effect on other handlers.

Martin York