Re: Uncaught exceptions not terminating my app
Re: Uncaught exceptions not terminating my app
- Subject: Re: Uncaught exceptions not terminating my app
- From: "Paul Sanders" <email@hidden>
- Date: Wed, 27 Jan 2010 19:01:16 -0000
As I went on something of a voyage of discovery with this, I thought I would write up my findings. Maybe this will save someone the pain I went through. It's rather a long post, sorry about that.
My basic tenet is that I want to catch unexpected exceptions and terminate my app in a way that produces a crash report containing a proper stack trace. I then use Uli Kusterer's UKCrashReporter class - somewhat modified - to send these off to my webserver when the app is relaunched. This has proven to be a very successful strategy, and I have found (and fixed) a lot of bugs this way. The alternative is to let exceptions pass by unnoticed, the effect of which is that in a code sequence A, B, C, D, an unexpected exception thrown in step B will mean that C and D are never executed (the app just mysteriously returns to the event loop) and that could cause some really obscure bugs in the field which it would be impossible to diagnose. I for one am not satisified with that.
But, it turns out, there is no way of trapping uncaught exceptions without also trapping exceptions that would otherwise be caught and handled within an @try block. This is because, for whatever reason, the delegate you pass to [NSExceptionHandler setDelegate:] is never called, even if you pass NSHandleTopLevelExceptionMask to [NSExceptionHandler setExceptionHandlingMask:], unless you also specify NSHandleOtherExceptionMask. The documentation promises that the delegate will be called, should an exception percolate through to the top level exception handler in NSApplication, but it isn't. At least, it isn't on Leopard. They seem to have fixed this on Snow Leopard.
However, you are no further forward even on Snow Leopard because [NSCarbonMenuImpl performActionWithHighlightingForItemAtIndex:] (which appears in the call stack when you respond to the user selecting an entry from the menu) catches and rethrows any otherwise uncaught exception. Result? You lose the stack trace in your crash report, rendering it useless.
So, here is my solution. This only works because the frameworks don't seem to ever use exception handlers to recover from exceptions gracefully, and neither do I:
1. Pass *all* mask bits, except for NSLogUncaughtSystemExceptionMask and NSHandleUncaughtSystemExceptionMask, to [NSExceptionHandler setExceptionHandlingMask:].
2. In your NSExceptionHandler delegate, crash the app when an exception arrives (I use "*(int * 1) = 0;" to do this), _except_for_ a few exceptions that seem to crop up every now and again in normal usage and must therefore be silently ignored (these look like bugs in the Cocoa frameworks to me).
3. Don't log or handle uncaught system exceptions because you get a better stack trace if you leave them alone. Also, if you log or handle these you loop forever on Tiger when something like a SIGBUS comes along.
4. Embed UKCrashReporter in your app (and write a suitable back-end for it on your webserver).
And now here's a reward for reading this far. Here's the list of exceptions I silently ignore in my NSExceptionHandler delegate:
-----------------------------------------------------------------------
NSString *nss_exception_name = [exception name];
NSString *nss_reason = [exception reason];
if ([nss_exception_name isEqualToString: NSAccessibilityException])
return YES;
if ([nss_exception_name isEqualToString: NSInternalInconsistencyException])
{
if (gIsTiger) // Happens on startup, apparently a Tiger bug
{
// __T ("lockFocus sent to a view whose window is ")
// __T ("deferred and does not yet have a corresponding platform window")
if ([nss_reason hasPrefix: @"lockFocus sent to a view whose window is deferred"])
return YES;
}
// Happens occasionally during testing (on 10.5.8), plus one crash reported in the field
if ([nss_reason hasPrefix: @"Invalid message sent to event \"NSEvent: type=LMouseUp"])
return YES;
if ([nss_reason hasPrefix: @"Invalid message sent to event \"NSEvent: type=RMouseUp"])
return YES;
// Seen this once in the field:
if ([nss_reason hasPrefix: @"Failed to get KeyCode from EventRef"])
return YES;
}
// Display bye-bye message to the user, maybe offer to save her work
* (int *) 1 = 0;
-----------------------------------------------------------------------
I'm sure there are one or two other special cases to add to this list. My users will inform me what they are in due course, and if I learn anything useful I will post it back to this thread.
Paul Sanders
http://www.alpinesoft.co.uk
PS: I just noticed you work for CodeWeavers, Ken. Salutations.
----- Original Message -----
From: "Ken Thomases" <email@hidden>
To: "Paul Sanders" <email@hidden>
Sent: Thursday, January 21, 2010 5:14 PM
Subject: Re: Uncaught exceptions not terminating my app
Your exception handler should be configured to only handle uncaught exceptions or those that would make it to the top-level catch. In other words, I think you should exclude NSHandleOtherExceptionMask from your handling mask, because it requests handling of caught exceptions.
Regards,
Ken
_______________________________________________
Cocoa-dev mailing list (email@hidden)
Please do not post admin requests or moderator comments to the list.
Contact the moderators at cocoa-dev-admins(at)lists.apple.com
Help/Unsubscribe/Update your Subscription:
This email sent to email@hidden