62

I have one crash in my iPhone application that does throw an NSException. The crash reports are completely ambiguous in where the error is and what exactly is causing it. Is there a smart way for me to set a top level exception handler somewhere to see what is causing it? I can't replicate the problem myself, but a few of my beta users certainly can.

What's a smart way to handle a problem of this nature?

Mark A. Donohoe
  • 28,442
  • 25
  • 137
  • 286
Coocoo4Cocoa
  • 48,756
  • 50
  • 150
  • 175

6 Answers6

85

It seems like you are asking two questions here: how to set a top level exception handler; and how to deal with the issue of determining what the root cause is.

Catching the exception can be done in a few different ways, but for this the best approach would appear to be to set an exception handler using NSSetUncaughtExceptionHandler.

When an exception is raised in your app, it is handled by a default exception handler. This handler does nothing more than log a message to the console before the app closes. You can over-ride this by setting you own custom exception handler using the function stated above. The best place to do this would be in the app delegate applicationDidFinishLaunching: method.

- (void)applicationDidFinishLaunching:(UIApplication *)application
{
    NSSetUncaughtExceptionHandler(&myExceptionHandler);
}

Once you've set a custom handler, you'll want to expand on the default output in helping you determine what the cause is.

void myExceptionHandler(NSException *exception)
{
    NSArray *stack = [exception callStackReturnAddresses];
    NSLog(@"Stack trace: %@", stack);
}

Unfortunately compared to OSX the iPhone appears quite limited in respect to producing a nice stack trace. The code above will produce some seemingly junk output; however, you can run this output through the atos tool, and you should be able to generate a useful stack trace from it.

Another option is to follow the instructions on this article which will help to produce a nice stack trace automatically.

As this is going out to beta testers you may have to tinker about to get it working for you.

You say that you've not been able to replicate the problem yourself, only your users. In this case you may find this technical note from Apple useful:

https://developer.apple.com/library/content/technotes/tn2151/_index.html

UPDATE: While this post still contains useful info, some of the links it contains are dead irreversibly. It is advised to use the info from this alternative post.

Cœur
  • 37,241
  • 25
  • 195
  • 267
Paul McCabe
  • 1,554
  • 9
  • 10
  • Google Toolbox for Mac's GTMStackTrace is a big help sometimes :) – rpetrich Aug 16 '09 at 05:22
  • 2
    Why iterate through the stack yourself, when you can do NSLog(@"%@" stack) ? – AlBlue Aug 16 '09 at 11:22
  • Based on the lack of feedback I assume that this is still a problem. I've added a reference to Apple technical notes that may help. – Paul McCabe Aug 28 '09 at 13:21
  • How many cycles does myExceptionHandler have to run? I'm trying to show a UIAlertView but the app completely dies before it shows up. Can you force myExceptionHandler to hog more cycles? – Mark Dec 03 '10 at 01:36
  • shouldn't that be a function, i.e., void myExceptionHandler (NSException *exception) – Grady Player Jul 10 '11 at 18:17
  • @mark Alternatively you can write a boolean to NSUserDefaults, like applicationDidCrash = YES. Then the next time the app starts up, check that flag and present an alert and run any necessary code. – nrj Oct 10 '11 at 22:15
  • Will this solution conflict with HockeyKit and/or QuincyKit? – Julian Jun 22 '12 at 16:17
  • Raising the dead: the link to rel.me seems broken. Does anybody know an alternative address? – Rob van der Veer Jul 29 '13 at 07:05
  • Your code prints something like `Exception stack: (0x1b35efef8 0x1b27bda40 0x1b34f4ac4 0x1b3f68b6c 0x1b3f9e528 0x100dc6950 0x100dc9b8c 0x100dc60e4 0x102304dac 0x1023057f8 0x1023851ec 0x1011e8ec4 0x1b30276c8 0x1b3028484 0x1b2fcb4d8 0x1b2fd7a60 0x1b2fd8308 0x1b320a190 0x1b320cd00)` But I want to see actual string path. – Sazzad Hissain Khan Oct 11 '18 at 09:14
12

If you are planning to do it on your own you could use one of these approaches

Approach1:

void onUncaughtException(NSException* exception)
{
//save exception details
}

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
  NSSetUncaughtExceptionHandler(&onUncaughtException);
  //Add coding to find if any exception has occurred from saved details if so send it to server or ask user to comment on the issue.
//Rest of the coding
}

Approach2:

void onUncaughtException(NSException* exception)
{

//Save exception details

}

int main(int argc, char *argv[])
{
    @autoreleasepool {

        NSSetUncaughtExceptionHandler(&onUncaughtException);

        return UIApplicationMain(argc, argv, nil, NSStringFromClass([SGGI_AppDelegate class]));
    }
}



-(BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
 {
      //Add coding to find if any exception has occurred from saved details if so send it to server or ask user to comment on the issue.
    //Rest of the coding
 }

Approcach3:

int main(int argc, char *argv[])
{
    @autoreleasepool {

        @try {

            return UIApplicationMain(argc, argv, nil, NSStringFromClass([SGGI_AppDelegate class]));
        }
        @catch (NSException *exception) {      
        //Save the exception
        }
        @finally {
        }

    }
}

-(BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
 {
      //Add coding to find if any exception has occurred from saved details if so send it to server or ask user to comment on the issue.
    //Rest of the coding
 }

Note:

  • In my perspective don't try to send the exception details to server at the time of crashing send it when he starts the app again.

  • If you are going to use NSUserDefaults to save exception details then you have to synchronise it at the time of crashing otherwise it won't persist.

The following code snippet does the job.

   - (void)applicationWillTerminate:(UIApplication *)application
   {
       [[NSUserDefaults standardUserDefaults]synchronize];
   }
  • If you prefer to save it on sqlite db then it persists itself no need to call anything to persist at the time of crashing
Durai Amuthan.H
  • 31,670
  • 10
  • 160
  • 241
5

In XCode, you should always set a global breakpoint for objc_exception_throw. Then you (usually) get a far more meaningful stack trace as to what is actually trying to throw an exception.

You can still get exceptions that originate in timer code or other places without your own code anywhere in the trace, but if you look at the method chain you can usually figure out generally what the exception is about (like having a notification sent where the target is gone).

Kendall Helmstetter Gelner
  • 74,769
  • 26
  • 128
  • 150
3

Another option for tracking crash reports is Plausible CrashReporter, open source code to automatically send you crash reports from the field.

There's also the CrashReporterDemo, another open source option that is a combination of Plausible CrashReporter and some server code to better track crash reports.

And lastly, there's MacDevCrashReporter, a service that appears to have similarities to iOSExceptional.com, suggested in another answer. I have no idea what their terms of service are, as I've not signed up for the beta. Definitely worth checking before getting in too deeply.

Matthew Frederick
  • 22,245
  • 10
  • 71
  • 97
2

Have you tried NSSetUncaughtExceptionHandler?

kperryua
  • 10,524
  • 1
  • 38
  • 24
2

Check out Crittercism. It goes above and beyond what you're asking of here in that it lets you get this information for all users using your app, so you should be able to see your own crash.

You can also upload the DYSM for your particular build and it will automatically symbolicate the crash for you on their website. That should provide you with the clearest stack-trace without being hooked up to the debugger.

You might also want to make sure you are set to break on Objetive-C exceptions. In Xcode 4, in the breakpoints tab, you can add a Breakpoint Exception that breaks on both/either C++ and Obj-C exceptions. Without this on, most stack traces for exceptions thrown are pretty unhelpful.

Good luck!

Mark
  • 7,306
  • 8
  • 45
  • 87