Symbolicating iOS Crash Logs
During iPhone app beta testing, and even when you have apps on the App Store, it’s often difficult to reproduce crashes reported from users. Luckily, iOS devices keep logs of each crash1 and can even tell you the exact line of code that caused the problem. Let’s take a look at a crash log for MyApp:
Exception Type: EXC_BAD_ACCESS (SIGBUS)
Thread 0 name: Dispatch queue: com.apple.main-thread
Thread 0 Crashed:
0 libobjc.A.dylib 0x313fec98 0x313fc000 + 11416
1 MyApp 0x00019c12 0x1000 + 101394
2 MyApp 0x0000a1cc 0x1000 + 37324
3 MyApp 0x0000c474 0x1000 + 46196
4 libdispatch.dylib 0x33e9d8e0 0x33e92000 + 47328
5 libdispatch.dylib 0x33e991ee 0x33e92000 + 29166
6 CoreFoundation 0x3568e934 0x35616000 + 493876
7 CoreFoundation 0x3561eebc 0x35616000 + 36540
8 CoreFoundation 0x3561edc4 0x35616000 + 36292
9 GraphicsServices 0x343cd418 0x343c9000 + 17432
10 GraphicsServices 0x343cd4c4 0x343c9000 + 17604
11 UIKit 0x3608ad62 0x3605c000 + 191842
12 UIKit 0x36088800 0x3605c000 + 182272
13 MyApp 0x000024ec 0x1000 + 5356
14 MyApp 0x000024ac 0x1000 + 5292
How are we supposed to tell anything from that? Only the hex symbols of the objects are there, so we need to symbolicate2 them. I am writing this short guide because Apple hasn’t documented this process very well, and no single article online had all the information I needed to get the job done. This post assumes a certain ability to debug your code too once you have the log symbolicated.
First, and most importantly, you need to have the .app and .dSYM file for the build that generated the crash log. This is why it’s very important to archive every build you distribute3, whether it be ad hoc or via the App Store. If you don’t have the original build, you’re out of luck.
I find it’s easiest to have everything, the .app, .dSYM, and .crash files, in the same directory. Next, we have to run a shell command called symbolicate which comes with Xcode. It’s a long path, so I recommend creating a bash/zsh alias for this, here’s mine:
alias symbolicate="/Developer/Platforms/iPhoneOS.platform/Developer/Library/\
PrivateFrameworks/DTDeviceKit.framework/Versions/A/\
Resources/symbolicatecrash -v"
Once that’s set in your shell, you can run the following, replacing the appropriate values of course:
symbolicate "MyApp_2011-05-10-170924-iPhone.crash" "MyApp.app"
Now you should see a lovely, symbolicated crash log spat out into the terminal:4
Exception Type: EXC_BAD_ACCESS (SIGBUS)
Thread 0 name: Dispatch queue: com.apple.main-thread
Thread 0 Crashed:
0 libobjc.A.dylib 0x33b24c98 objc_msgSend + 16
1 MyApp 0x00019c12 -[TwoLineNavBarTitleView setLine2:]
(TwoLineNavBarTitleView.m:43)
2 MyApp 0x0000a1cc -[ListViewController
listModelDidFinishQueryingForRadius:]
(ListViewController.m:160)
3 MyApp 0x0000c474 __45-[ListViewModel
filterParksBasedOnCategories]
(ListViewModel.m:129)
4 libdispatch.dylib 0x33d108e0 _dispatch_call_block_and_release + 4
5 libdispatch.dylib 0x33d0c1ee _dispatch_main_queue_callback + 306
6 CoreFoundation 0x3039f934 __CFRunLoopRun + 1328
7 CoreFoundation 0x3032febc CFRunLoopRunSpecific + 224
8 CoreFoundation 0x3032fdc4 CFRunLoopRunInMode + 52
9 GraphicsServices 0x35571418 GSEventRunModal + 108
10 GraphicsServices 0x355714c4 GSEventRun + 56
11 UIKit 0x358c7d62 -[UIApplication _run] + 398
12 UIKit 0x358c5800 UIApplicationMain + 664
13 MyApp 0x000024ec main (main.m:14)
14 MyApp 0x000024ac 0x1000 + 5292
Here I can see which method was called (setLine2: in my custom view class) and that it was called from line 43 of the same class. Much more helpful. Now I can fix my over-released memory reference and call this bug squashed.
- How you get the logs is another story. They're stored on the user's computer after syncing with iTunes. If you have a line of communication with your users, it's usually easy for them to find the relevant logs and send them to you. iTunes Connect supposedly gathers crash logs for you but I've never seen any in there. I haven't experimented with ways of in-app crash reporting yet. ↩
- Isn't that a cool word? ↩
- Xcode's built-in Archive function is great for this, especially when paired with Time Machine. ↩
- I typically like to use the -o tack on the symbolicate command to output the results to a file for easier reading and organization. ↩