Crash Reporter for iPhone Applications (Part 1)

crashDuck.png

I’m getting ready to send my iPhone Application to the first beta testers and so I started thinking about crash reports.

On the Mac you have two options to get the crash report of your application. The first one is to check on startup if there is a new crash report file in ~/Library/Logs/CrashReporter for your application and simply send it. (Example: HDCrashReporter , UKCrashReporter)

The other option is to use NSExceptionHandler, which let you report the problem as it happens. (Example: A simple debugging tool for Cocoa)

On the iPhone you can’t access the crash report file form your application because it’s running in a sandbox (rdar://problem/6296554). iTunes synchronizes the crash reports of all applications, so although a user could in theory send it to you, this seems rather complicated to me (Locations of Crash Logs).

So what about NSExceptionHandler? Well, there isn’t a NSExceptionHandler in the iPhone SDK. But wait, there is a NSUncaughtExceptionHandler!

/* GeSHi (C) 2004 – 2007 Nigel McNie (http://qbnz.com/highlighter) */
.objc .de1, .objc .de2 {font-family: ‘Courier New’, Courier, monospace; font-weight: normal; font-size: 11px; }
.objc {font-family: monospace;}
.objc .imp {font-weight: bold; color: red;}
.objc li {background: #f8f8f8;}
.objc li.li2 {background: #f8f8f8;}
.objc .kw1 {color: #0000ff;}
.objc .kw2 {color: #0000ff;}
.objc .kw3 {color: #0000dd;}
.objc .kw4 {color: #0000ff;}
.objc .kw5 {color: #0000ff;}
.objc .kw6 {color: #0000ff;}
.objc .co1 {color: #ff0000;}
.objc .co2 {color: #339900;}
.objc .coMULTI {color: #ff0000; font-style: italic;}
.objc .es0 {color: #666666; font-weight: bold;}
.objc .br0 {color: #002200;}
.objc .st0 {color: #666666;}
.objc .nu0 {color: #0000dd;}
html>body .entry .objc ol li { margin: 0 0 0 0; }
.syntax { padding-left: 15px; background-color: #E8E8E8; text-align:left; }

  1. void MyUncaughtExceptionHandler(NSException *exception) {
  2.     NSArray *callStackArray = [exception callStackReturnAddresses];
  3.     int frameCount = [callStackArray count];
  4.     void *backtraceFrames[frameCount];
  5.    
  6.     for (int i=0; i<frameCount; i++) {
  7.         backtraceFrames[i] = (void *)[[callStackArray objectAtIndex:i] unsignedIntegerValue];
  8.     }
  9.    
  10.     // report the exception
  11. }
  12.  
  13. @implementation AppDelegate
  14.  
  15. (void)applicationDidFinishLaunching:(UIApplication *)application {
  16.     NSSetUncaughtExceptionHandler(&MyUncaughtExceptionHandler);
  17. }
  18.  
  19. @end

Now you can get the backtrace in MyUncaughtExceptionHandler with backtrace_symbols(3). (Example)
You still have to symbolicate the crash reports.

As a big fan of Fogbugz, I also wanted to use BugzScout. It’s just a HTTP POST, but here is the code:

  1. void bugzScout(NSString *description, NSString *extra) {
  2.         NSMutableString *post = [NSMutableString string];
  3.         [post appendString:@"ScoutUserName="];
  4.         [post appendString:urlEncodeValue(@"Your BugzScout User")];
  5.        
  6.         [post appendString:@"&ScoutProject="];
  7.         [post appendString:urlEncodeValue(@"Your Project")];
  8.        
  9.         [post appendString:@"&ScoutArea="];
  10.         [post appendString:urlEncodeValue(@"Your Area")];
  11.        
  12.         [post appendString:@"&Description="];
  13.         [post appendString:urlEncodeValue(description)];
  14.        
  15.         [post appendString:@"&Extra="];
  16.         [post appendString:urlEncodeValue(extra)];
  17.        
  18.         [post appendString:@"&ScoutDefaultMessage=&FriendlyResponse=0&ForceNewBug=0"];
  19.        
  20.         //NSLog(post);
  21.        
  22.         NSData *postData = [post dataUsingEncoding:NSASCIIStringEncoding allowLossyConversion:YES];
  23.         NSString *postLength = [NSString stringWithFormat:@"%d", [postData length]];
  24.        
  25.         NSURL *webServiceURL = [NSURL URLWithString:@"https://yourcompany.fogbugz.com/ScoutSubmit.asp&quot;];
  26.         NSMutableURLRequest *req = [NSMutableURLRequest requestWithURL:webServiceURL];
  27.        
  28.         [req setHTTPMethod:@"POST"];
  29.         [req addValue:@"application/x-www-form-urlencoded" forHTTPHeaderField: @"Content-Type"];
  30.         [req addValue:postLength forHTTPHeaderField: @"Content-Length"];
  31.         [req setHTTPBody:postData];
  32.        
  33.         [NSURLConnection sendSynchronousRequest:req returningResponse:nil error:nil];   
  34. }
  35.  
  36. NSString *urlEncodeValue(NSString *str) {
  37.         CFStringRef urlString = CFURLCreateStringByAddingPercentEscapes(kCFAllocatorDefault,
  38.                 (CFStringRef)str, NULL, CFSTR(";/?:@&=+$,"), kCFStringEncodingUTF8);
  39.         return [(NSString *)urlString autorelease];
  40. }

Crash Reporter for iPhone Applications (Part 1)
Crash Reporter for iPhone Applications (Part 2)

13 thoughts on “Crash Reporter for iPhone Applications (Part 1)

  1. Chris…

    I wonder if you could give a bit more detail on how to symbolicate the stack trace you are generating. I managed to get a reasonable stack trace by using the backtrace_symbols function you mentioned, however the symbols for my application (the ones i need) are missing. When i attempt to symbolicate the stack trace generated by the function above i get the following error from the symbolicate utility:

    joshua$ symbolicatecrash maildump.txt ICONtact.app.dSYM
    No crash report version in maildump.txt at /Users/joshua/bin/symbolicatecrash line 719.

    Running the symbolicate utility against a full from the iPhone crash dump however works fine.

  2. Hi Chris,
    Have you managed to find a better solution (as mentioned in the above comment)? I’m trying to get this to work with my iPhone application, but I’m a little confused as to how you use this atos tool from the phone. Are you calling the system atos application directly from within your app?
    I’m anxious to get this working as it would greatly improve our debugging cycle. This blog is definitely a great starting point. Perhaps one more post would be useful which details the entirety of the procedure in one place.
    Thanks in advance.

  3. Hi Glenn,

    you don’t use the atos tool on the iphone, you use it on your developer box. After you build the application you see in your build folder a dSYM folder which include the needed information for atos.

    I still haven’t automated this…

    Christopher

  4. Hi Chris,

    Thanks for the post! I’ve just spotted there’s an error in the MyUncaughtExceptionHandler code:

    for (int i=0; i<[callStackArray cout]; i++) {

    …should be “count”, not “cout”. (I’m guessing that you want to use frameCount as the loop’s check too – it’s unused otherwise.)

    Ironically, using the code as it is causes an exception in the exception handler, resulting in an infinite loop of exceptions 🙂

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s