how to love (and read) crash reports
Crash Reports are essential, if often opaque, guides to problems in your app that every developer has to deal with from time to time.
For small enterprise developers like myself, commercial analytics aren’t really an option. And while there are open source alternatives like UKCrashReporter, I’ve been lucky enough to suffer relatively few crashes and typically just ask users to manually submit crash reports (stored in ~/Library/Logs/DiagnosticReports) as and when they occur.
Choosing a method of collecting crash reports is one thing, but scouring them for useful information is quite another, and the reams of data often included can seem a little overwhelming.
On a good day, your crash report may include a direct reference to the method or function that’s caused the issue, like this one from an early version of my troubleshooting app DetectX Swift:
On other days, though, you might get presented with nothing but a bunch of unhelpful memory addresses:
It’s at this point that you need to enlist the help of the built-in command-line tool atos. In the remainder of this post, we’re going to learn what it does and how to use it to find the line in our source code that caused the crash.
How does atos help?
The beauty of
atos is that it will give you a reference to the source file, function and even line number in your code where the crash occurred. Given that it can do this for a crash that occurred on another user’s machine that you can’t reproduce locally and don’t have access to, this seems like nothing short of magic.
Here’s an example of what
atos might provide from a crash log like the one above:
ViewController.checkReg(isCli:) (in DetectX Swift) (ViewController.swift:4042)
The output from
atos tells me the name of the Swift source file, the name of the function and — here’s the truly wonderous part — even the line number: 4042.
This is all part of the magic of symbollication, which is such an esoteric word I’m not even going to try to spell it again :-). Instead, let’s just get down to the practical nitty-gritty of how to get this kind of data out of our users’ crash reports.
How to use atos
The method is fairly simple and goes like this:
1. Create a folder called “CrashWork”.
2. Go to Xcode’s ‘Organizer’, and right-click on the archive of the version of the app that crashed.
Choose “Show in Finder”. That takes you to the
.xcarchive file in Finder, from which right-click again and choose “Show Package Contents” to open the package:
3. Click on the
.dSYM file and make a copy of it. Switch back to your CrashWork folder and paste the copy in there. Grab a copy of the same version of your app that crashed (you can also get that from the Products folder in the .xcarchive package, if you don’t have one handy elsewhere) and place it in the same folder:
Important: Be sure you’re working with the same version of your app as the user;
atos needs that to match up the addresses in the crash report, and any other version will produce incorrect results or an error.
4. It’s now time to head on over to the Terminal.
cd into your CrashWork directory:
5. The format of the command is generally going to be like this (see man atos for options):
atos -o <path to executable> -arch x86_64 -l <load address> <address>
<path to executable> is the path all the way to the Mach-O binary in your app bundle in the CrashWork folder, so the path in my example looks like this:
atos -o DetectX\ Swift.app/Contents/MacOS/DetectX\ Swift -arch x86_64 -l <load address> <address>
You need to be careful to get the
<load address> and the
<address> the right way around. They are the reverse order of what you see in the crash log:
Now my complete example looks like this:
atos -o DetectX\ Swift.app/Contents/MacOS/DetectX\ Swift -arch x86_64 -l 0x10a10e000 0x000000010a16a601
6. Hitting ‘return’ produces the magic: