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 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.