Demystifying iOS Application Crash Logs

This is a blog post by Soheil Moayedi Azarpour, an independent iOS developer. You can also find him on Google+. Have you ever had the following experience as an app developer? Before you submit your app, you perform a lot of testing to make sure your app runs flawlessly. It works fine on your device, […] By Soheil Azarpour.

Leave a rating/review
Save for later
Share
You are currently viewing page 5 of 6 of this article. Click here to view the first page.

Scenario 3: Another Bug On the Table

Another user complains saying, “I can’t delete bookmarked masters from the bookmarks view…” And another email about the same issue reads, “If I try to delete a master in bookmarks, the app crashes…”

By now, you are used to these emails not being very helpful. To the crash logs!

Incident Identifier: 5B62D681-D8FE-41FE-8D52-AB7E6D6B2AC7
CrashReporter Key:   5a56599d836c4f867f6eec76afee451bf9ae5f31
Hardware Model:      iPhone4,1
Process:         Rage Masters [20088]
Path:            /var/mobile/Applications/B2121A89-3D1F-4E61-BB18-5511E1DC150F/Rage Masters.app/Rage Masters
Identifier:      Rage Masters
Version:         ??? (???)
Code Type:       ARM (Native)
Parent Process:  launchd [1]

Date/Time:       2012-11-03 13:38:45.762 -0400
OS Version:      iOS 6.0 (10A403)
Report Version:  104

Exception Type:  EXC_CRASH (SIGABRT)
Exception Codes: 0x0000000000000000, 0x0000000000000000
Crashed Thread:  0

Last Exception Backtrace:
0   CoreFoundation                	0x36bff29e __exceptionPreprocess + 158
1   libobjc.A.dylib               	0x34f0f97a objc_exception_throw + 26
2   CoreFoundation                	0x36bff158 +[NSException raise:format:arguments:] + 96
3   Foundation                    	0x346812aa -[NSAssertionHandler handleFailureInMethod:object:file:lineNumber:description:] + 86
4   UIKit                         	0x37f04b7e -[UITableView(_UITableViewPrivate) _endCellAnimationsWithContext:] + 7690
5   UIKit                         	0x3803a4a2 -[UITableView deleteRowsAtIndexPaths:withRowAnimation:] + 22
6   Rage Masters                  	0x000fd9ca -[RMBookmarksViewController tableView:commitEditingStyle:forRowAtIndexPath:] (RMBookmarksViewController.m:68)
7   UIKit                         	0x3809a5d4 -[UITableView(UITableViewInternal) animateDeletionOfRowWithCell:] + 80
8   UIKit                         	0x37fbb0a8 -[UIApplication sendAction:to:from:forEvent:] + 68
9   UIKit                         	0x37fbb05a -[UIApplication sendAction:toTarget:fromSender:forEvent:] + 26
10  UIKit                         	0x37fbb038 -[UIControl sendAction:to:forEvent:] + 40
11  UIKit                         	0x37fba8ee -[UIControl(Internal) _sendActionsForEvents:withEvent:] + 498
12  UIKit                         	0x37fbb0a8 -[UIApplication sendAction:to:from:forEvent:] + 68
13  UIKit                         	0x37fbb05a -[UIApplication sendAction:toTarget:fromSender:forEvent:] + 26
14  UIKit                         	0x37fbb038 -[UIControl sendAction:to:forEvent:] + 40
15  UIKit                         	0x37fba8ee -[UIControl(Internal) _sendActionsForEvents:withEvent:] + 498
16  UIKit                         	0x37fbade4 -[UIControl touchesEnded:withEvent:] + 484
17  UIKit                         	0x37ee35f4 -[UIWindow _sendTouchesForEvent:] + 520
18  UIKit                         	0x37ed0804 -[UIApplication sendEvent:] + 376
19  UIKit                         	0x37ed011e _UIApplicationHandleEvent + 6150
20  GraphicsServices              	0x3708359e _PurpleEventCallback + 586
21  GraphicsServices              	0x370831ce PurpleEventCallback + 30
22  CoreFoundation                	0x36bd416e __CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE1_PERFORM_FUNCTION__ + 30
23  CoreFoundation                	0x36bd4112 __CFRunLoopDoSource1 + 134
24  CoreFoundation                	0x36bd2f94 __CFRunLoopRun + 1380
25  CoreFoundation                	0x36b45eb8 CFRunLoopRunSpecific + 352
26  CoreFoundation                	0x36b45d44 CFRunLoopRunInMode + 100
27  GraphicsServices              	0x370822e6 GSEventRunModal + 70
28  UIKit                         	0x37f242fc UIApplicationMain + 1116
29  Rage Masters                  	0x000fb004 main (main.m:16)
30  libdyld.dylib                 	0x3b630b1c start + 0

This looks very similar to the previous crash log. It’s another SIGABRT exception. Perhaps you wonder if it’s the same issue: sending a message to an object that has not implemented the method?

Let’s traverse through the backlog and see what methods were called. Start from the bottom. The last call to your code in Rage Masters is at frame 6:

6    Rage Masters    0x00088c66 -[RMBookmarksViewController tableView:commitEditingStyle:forRowAtIndexPath:] (RMBookmarksViewController.m:68)

Well, this is a UITableViewDataSource method. Huh?! You are pretty sure Apple has implemented this method – you can override it, but it is not like it hasn’t been implemented. Plus, it is an optional delegate method. So the problem isn’t a call to an unimplemented method.

Take a look at the frames after that call:

3    Foundation    0x346812aa -[NSAssertionHandler handleFailureInMethod:object:file:lineNumber:description:] + 86
4    UIKit         0x37f04b7e -[UITableView(_UITableViewPrivate) _endCellAnimationsWithContext:] + 7690
5    UIKit         0x3803a4a2 -[UITableView deleteRowsAtIndexPaths:withRowAnimation:] + 22

In frame 5, UITableView is calling another method of its own, deleteRowsAtIndexPaths:withRowAnimation: and then _endCellAnimationsWithContext: is called, which looks like an internal Apple method. Then the Foundation framework raises an exception, handleFailureInMethod:object:file:lineNumber:description:.

Putting the above together with the user complaints, it looks as if you are dealing with a buggy deletion procedure in UITableView. Go to Xcode. Do you know where to go? Can you tell from the crash log? It’s line 68 in RMBookmarksViewController.m:

- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath {
    
    [self.tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationAutomatic];
}

Can you tell where the problem is? I’ll wait while you take a look.

You got it! How about the data source? The code deletes a row in the table view, but doesn’t change the data source behind it. To fix this, replace the above method with the following one:

-(void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath {
    
    RMMaster *masterToDelete = [bookmarks objectAtIndex:indexPath.row];
    [bookmarks removeObject:masterToDelete];
    [[RMBookmarks sharedBookmarks] unbookmarkMaster:masterToDelete];
    
    [self.tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationAutomatic];
}

That should do it! Take that, you nasty bug!! BIFF!!! BANG!!!! CRASH!!!!!

Scenario 4: Leggo Those Licks!

The email says, “My app crashes when a rage master is licking a lollipop…” Another user writes, “I tell the rage master to lick the lollipop few times, and then the app crashes!”

Here’s the crash log:

Incident Identifier: 081E58F5-95A8-404D-947B-5E104B6BC1B1
CrashReporter Key:   5a56599d836c4f867f6eec76afee451bf9ae5f31
Hardware Model:      iPhone4,1
OS Version:          iPhone OS 6.0 (10A403)
Kernel Version:      Darwin Kernel Version 13.0.0: Sun Aug 19 00:28:05 PDT 2012; root:xnu-2107.2.33~4/RELEASE_ARM_S5L8940X
Date:                2012-11-03 13:39:59 -0400
Time since snapshot: 4353 ms

Free pages:        968
Active pages:      7778
Inactive pages:    4005
Throttled pages:   92319
Purgeable pages:   0
Wired pages:       23347
Largest process:   Rage Masters

Processes
     Name                    <UUID>                       rpages       recent_max       [reason]          (state)

             lsd <6a9f5b5f36b23fc78f87b6d8f1f49a9d>          331              331         [vm]         (daemon) (idle)
            afcd <b0aff2e7952e34a9882fec81a8dcdbb2>          141              141         [vm]         (daemon) (idle)
    itunesstored <4e0cd9f873de3435b4119c48b2d6d13d>         1761             1761         [vm]         (daemon) (idle)
softwareupdatese <2bc4b5ae016431c98d3b34f81027d0ae>          311              311         [vm]         (daemon) (idle)
          Amazon <4600481f07ec3e59a925319b7f67ba14>         2951             2951         [vm]         (suspended)
       accountsd <ac0fce15c1a2350d951efc498d521ac7>          519              519         [vm]         (daemon) (idle)
coresymbolicatio <edba67001f76313b992056c712153b4b>          126              126         [vm]         (daemon) (idle)
           Skype <504cf2fe60cb3cdea8273e74df09836b>         3187             3187         [vm]         (background)
      MobileMail <bff817c61ce33c85a43ea9a6c98c29f5>        14927            14927         [vm]         (continuous)
       MobileSMS <46778de076363d67aeea207464cfc581>         2134             2134         [vm]         (background)
     MobilePhone <3fca241f2a193d0fb8264218d296ea41>         2689             2689         [vm]         (continuous)
      librariand <c9a9be81aa9632f0a913ce79b911f27e>          317              317         [vm]         (daemon)
             kbd <3e7136ddcefc3d77a01499db593466cd>          616              616         [vm]         (daemon)
            tccd <eb5ddcf533663f8d987d67cae6a4c4ea>          224              224         [vm]         (daemon)
    Rage Masters <90b45d6281e934209c5b06cf7dc4d492>        28591            28591         [vm]         (frontmost) (resume)
            ptpd <04a56fce67053c57a7979aeea8e5a7ea>          879              879                      (daemon)
   iaptransportd <f784f30dc09d32078d87b450e8113ef6>          230              230                      (daemon)
       locationd <892cd1c9ffa43c99a82dba197be5f09e>         1641             1641                      (daemon)
         syslogd <cbef142fa0a839f0885afb693fb169c3>          237              237                      (daemon)
    mediaserverd <80657170daca32c9b8f3a6b1faac43a2>         4869             4869                      (daemon)
     dataaccessd <2a3f6a518f3f3646bf35eddd36f25005>         1786             1786                      (daemon)
      aosnotifyd <d4d14f2914c3343796e447cfef3e6542>          549              549                      (daemon)
           wifid <9472b090746237998cdbb9b34f090d0c>          455              455                      (daemon)
     SpringBoard <27372aae101f3bbc87804edc10314af3>        18749            18749                     
      backboardd <5037235f295b33eda98eb5c72c098858>         5801             5801                      (daemon)
  UserEventAgent <6edfd8d8dba23187b05772dcdfc94f90>          601              601                      (daemon)
    mediaremoted <4ff39c50c684302492e396ace813cb25>          293              293                      (daemon)
     pasteboardd <8a4279b78e4a321f84a076a711dc1c51>          176              176                      (daemon)
springboardservi <ff6f64b3a21a39c9a1793321eefa5304>            0                0                      (daemon)
    syslog_relay <45e9844605d737a08368b5215bb54426>            0                0                      (daemon)
      DTMobileIS <23303ca402aa3705870b01a9047854ea>            0                0                      (daemon)
notification_pro <845b7beebc8538ca9ceef731031983b7>          169              169                      (daemon)
    syslog_relay <45e9844605d737a08368b5215bb54426>            0                0                      (daemon)
             ubd <74dc476d1785300e9fcda555fcb8d774>          976              976                      (daemon)
        twitterd <4b4946378a9c397d8250965d17055b8e>          730              730                      (daemon)
         configd <4245d73a9e96360399452cf6b8671844>          809              809                      (daemon)
   absinthed.N94 <7f4164c844fa340caa940b863c901aa9>           99               99                      (daemon)
filecoordination <fbab576f37a63b56a1039153fc1aa7d8>          226              226                      (daemon)
       distnoted <a89af76ec8633ac2bbe99bc2b7964bb0>          137              137                      (daemon)
            apsd <94d8051dd5f5362f82d775bc279ae608>          373              373                      (daemon)
        networkd <0032f46009f53a6c80973fe153d1a588>          219              219                      (daemon)
      aggregated <8c3c991dc4153bc38aee1e841864d088>          112              112                      (daemon)
        BTServer <c92fbd7488e63be99ec9dbd05824f5e5>          522              522                      (daemon)
   fairplayd.N94 <7bd896bd00783a48906090d05cf1c86a>          210              210                      (daemon)
       fseventsd <996cc4ca03793184aea8d781b55bce08>          384              384                      (daemon)
         imagent <1e68080947be352590ce96b7a1d07b2f>          586              586                      (daemon)
   mDNSResponder <3e557693f3073697a58da6d27a827d97>          295              295                      (daemon)
       lockdownd <ba1358c7a8003f1b91af7d5f58dd5bbe>          389              389                      (daemon)
          powerd <2d2ffed5e69638aeba1b92ef124ed861>          174              174                      (daemon)
      CommCenter <1f425e1e897d32e8864fdd8eeaa803a8>         2212             2212                      (daemon)
         notifyd <51c0e03da8a93ac8a595442fcaac531f>          211              211                      (daemon)
     ReportCrash <8c32f231b2ed360bb151b2563bcaa363>          337              337                      (daemon)

This log is very different from what you’ve seen so far!

This is a low memory crash log from iOS 6. As discussed earlier, low memory crash logs are different from other types of crash logs because they don’t point to a specific file or line of code. Instead, they paint a picture of the memory situation on the device at the time of the crash.

The header, at least, is similar to that of other crash logs: it provides the Incident Identifier, CrashReporter Key, Hardware Model, OS Version, and some other information.

The next section is specific to low memory crash logs:

  • Free pages refers to available memory. Each page is approximately 4KB, so in the log above, available memory is about 3,872 KB (or 3.9 MB).
  • Purgeable pages are those parts of memory that can be purged and reused. In the log above, it is 0KB.
  • Largest process refers to the application that was using most of the memory at the time of the crash, which in this case is your app!
  • Processes gives you a list of processes, along with their memory usage at the time of the crash. You are given the name of the process (first column), the unique identifier of the process (second column), and the number of pages being used by the process (third column). In the last column, State, you can see the state of each app. Usually, the app that caused the crash is the app with the frontmost state. Here it is Rage Masters, which was using 28591 pages (or 114.364 MB) – that’s a lot of memory!

Usually, the largest process and the frontmost app are the same, and are also the process that has caused the low memory crash. But you may see some instances where the largest process and the frontmost app are not the same. For example, if the largest process is SpringBoard, ignore it, because SpringBoard is the process that shows the apps on the home screen, the popups that appear when you double tap the home button, etc. and is always active.

When low memory situations happen, iOS sends a low memory warning to the active application and terminates background processes. If the frontmost app still continues to grow in memory, iOS jettisons it.

To find the reason for the low memory issues, you need to profile your app using Instruments. You won’t be doing that here, since we already have a tutorial for that. :] Instead, you’ll take a shortcut and just respond to the low memory warning notification that the app receives to solve this particular crash.

Switch to Xcode and go to RMLollipopLicker.m. This is where the lollipop licker view controller is implemented. Take a look at the source code:

#import "RMLollipopLicker.h"

#define COUNT 20

@interface RMLollipopLicker ()
@property (weak, nonatomic) IBOutlet UIProgressView *progressView;
@property (weak, nonatomic) IBOutlet UILabel *label;
@property (weak, nonatomic) IBOutlet UILabel *lickedTimeLabel;
@end

@implementation RMLollipopLicker {
    NSOperationQueue *queue;
    NSMutableArray *lollipops;
}

#pragma mark - Life cycle

- (void)viewDidLoad {
    [super viewDidLoad];
    
    self.progressView.progress = 0.0;
    self.label.text = [NSString stringWithFormat:@"Tap on run and I'll lick a lollipop %d times!", COUNT];
    self.lickedTimeLabel.text = @"";
    
    lollipops = [[NSMutableArray alloc] init];
    queue = [[NSOperationQueue alloc] init];
}

- (void)lickLollipop {
    NSURL *fileURL = [[NSBundle mainBundle] URLForResource:@"Lollipop" withExtension:@"plist"];
    NSDictionary *dictionary = [NSDictionary dictionaryWithContentsOfURL:fileURL];
    NSString *lollipop = [dictionary objectForKey:@"Lollipop"];
    [lollipops addObject:lollipop];
}

#pragma mark - IBActions

- (IBAction)doneButtonPressed:(id)sender {
    
    [self dismissViewControllerAnimated:YES completion:nil];
}

- (IBAction)runButtonPressed:(id)sender {
    
    [sender setEnabled:NO];
    [queue addOperationWithBlock:^{
        
        for (NSInteger i = 0 ; i <= COUNT ; i++) {
            [self lickLollipop];
            
            [[NSOperationQueue mainQueue] addOperationWithBlock:^{
                
                self.label.text = [NSString stringWithFormat:@"Licked a strawberry lollipop %d time(s)!", i];
                self.lickedTimeLabel.text = [NSString stringWithFormat:@"Licked the same lollipop %d time(s)!", lollipops.count-1];
                self.progressView.progress = (float)(i/COUNT);
                
                if (i >= COUNT) {
                    self.label.text = [NSString stringWithFormat:@"Tap on run and I'll lick a lollipop %d times!", COUNT];
                    self.progressView.progress = 0.0;
                    [sender setEnabled:YES];
                }
            }];
        }
    }];
    
}

@end

When the user taps the run button, the app starts a background operation, calls lickLollipop a number of times, and then updates the UI to reflect the number of licks. lickLollipop reads a big NSString from a property list (PLIST) and adds it to an array. This data is not crucial, and can be recreated without affecting the user experience.

It’s a good habit to take advantage of every situation where you can purge data and recreate it without adversely affecting the user experience. This frees up memory, making low memory warnings less likely.

So how can you improve the code here? Implement didReceiveMemoryWarning and get rid of the data in lollipops as follows:

-(void)didReceiveMemoryWarning {
    [lollipops removeAllObjects];
    [super didReceiveMemoryWarning];
}

That should be it!

Contributors

Over 300 content creators. Join our team.