In one of our iOS projects we used PLCrashReporter to capture crash reports and email them to one of our servers. Since then we have decided that Crashlytics does a much better job for a lot less work, and cost the same amount (FREE!). Because of this, we switched over to using Crashlytics, so I won’t explain how to set up PLCrashReporter, but the symbolication of crash reports can sometimes be essential.
In order to get things set up for symbolicating crash reports, you’ll need to use PLCrashUtil to convert the crash report into Xcode’s format.
Once the crash report is in Xcode’s format things are supposed to be quite simple.
Prerequisites
- PLCrashUtil – only required if you are using PLCrashReporter
- dSYMs
- Xcode
If you do not have the dSYMs and Xcode, there’s nothing you can do. You will have to obtain the dSYMs for the build that you received a crash report for. Usually you’ll have these if you are the one who archived the project. If you don’t have the dSYMs, you’ll probably need to contact whoever created the archive to get them. The easiest thing to do is just import the archive into Xcode.
Setup
- Download PLCrashReporter
- https://www.plcrashreporter.org/download
- Just download the zip version
- Unzip the package
- In the tools folder is plcrashutil
- If you’re going to be using plcrashutil frequently, you may want to place it in /usr/local/bin
- Place the following in ~/.bash_profile
-
12export DEVELOPER_DIR=/Applications/Xcode.app/Contents/Developeralias symbolicatecrash=/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/Library/PrivateFrameworks/DTDeviceKitBase.framework/Versions/A/Resources/symbolicatecrash
Converting Crash Reports
In order to symbolicate the crash, you must convert the *.plcrash file into a *.crash. Do this with PLCrashUtil
1 |
./plcrashutil convert --format=ios <crash report.plcrash> |
This will spit the *.crash file out into the console. If you want to output it to a file you can do the following
1 |
plcrashutil convert --format=ios <crash report.plcrash> > report.crash |
Or if you want to try and symbolicate the crash directly, you can use the following.
1 |
plcrashutil convert --format=ios <crash report.plcrash> | symbolicatecrash |
Once again this will just spit everything out into the terminal. This is not particularly useful, so you probably want to just save out the *.crash file and import it into Xcode.
Unfortunately the project we were using PLCrashReporter in was distributed in nonstandard ways. We distributed the ipa to the client, they resigned the ipa with their own certificate and distributed the application with their own enterprise account. This means that the crash reports we receive need a little editing in order to symbolicate correctly.
Unless you’re in a similar situation, you can skip to the next section. You can also skip to the last section if you want to just symbolicate things line by line.
Crash reports contain the following information
- Metadata
- Stack traces for each threads
- Binary Images
The Binary Images section will look something like this
1 2 3 4 5 6 7 |
Binary Images: 0x24000 - 0x22cfff +MyApp armv7s <cf7cceb9975c38c98bb19663add94f76> /var/mobile/Applications/734B2CFA-84BE-4EA6-9882-73DDAA583DBC/MyApp.app/MyApp 0x2ea6a000 - 0x2eb53fff RawCamera armv7s <bf399f99756e3fa992c2c0d7f2eb0049> /System/Library/CoreServices/RawCamera.bundle/RawCamera 0x2ec7f000 - 0x2ed80fff AVFoundation armv7s <759b362f09e53f37a2ec82372a95d1de> /System/Library/Frameworks/AVFoundation.framework/AVFoundation 0x2ed81000 - 0x2eda9fff libAVFAudio.dylib armv7s <0925efab4dd338e382aa5b10cdbed33f> /System/Library/Frameworks/AVFoundation.framework/libAVFAudio.dylib 0x2edaa000 - 0x2edaafff Accelerate armv7s <9340338f3cdf347abe4a88c2f59b5b12> /System/Library/Frameworks/Accelerate.framework/Accelerate ... |
Each line contains
- A few hex addresses
- Framework name
- Framework architecture
- Framework UUID
- Framework path
The framework UUID maps directly to a particular set of dSYMs. The resigning process that our client uses requires us to change the UUID for the MyApp framework. The only reason we can do this is because we know that the dSYMs for the ipa we send our client compatible with the dSYMs for the resigned ipa.
We need to find the UUID for the dSYMs we have, and put it into the crash file we have. Run the following command to get the UUID for the dSYMs that you know are correct for the distributed ipa.
1 |
dwarfdump --uuid <path to *.app.dSYM> | tr '[:upper:]' '[:lower:]' | tr -d '-' |
This will spit out something like this
1 2 |
UUID: 4c4d6e7552a73d33a0a71c8cddae22c1 (armv7) MyApp.app.dSYM/Contents/Resources/DWARF/MyApp UUID: cf7cceb9975c38c98bb19663add94f76 (armv7s) MyApp.app.dSYM/Contents/Resources/DWARF/MyApp |
Replace the UUID of your framework with the appropriate UUID from the output.
You should now be able to symbolicate things correctly.
Importing into Xcode
Once you have a *.crash file
- Open Xcode
- Go to the organizer
- In the upper left, select “Device Logs”
- Drag the *.crash file into the center pane
The crash report should get symbolicated automatically now. If it doesn’t, which seems to happen to me an awful lot, you’ll have to symbolicate the crash report manually.
Manual Symbolication
Sometimes, even when you’re certain you’ve done everything right, you’re still left with an unsymbolicated crash report. And eventually you reach the point where you’ve spent too much time trying to force Xcode to do what you want, and you need to just give up. When this happens, you can just use atos to query the dSYM for a particular memory address.
In the stack trace, you’ll see a line like this
1 |
MyApp 0x0006c063 0x24000 + 295011 |
The first hex code is the memory address. The second hex code is the load address. The last number is probably important somehow.
To use atos execute the following
1 |
atos -arch <appropriate architecture> -o <path to MyApp.app.dSYM>/Contents/Resources/DWARF/MyApp -l <load address> |
In this case it would be 0x24000 for the load address.
This will put atos into interactive mode. Just type in an address, and it’ll output what’s at that memory address. In this case, you’d enter 0x0006c063. Once you’re done you can just hit ctrl+c to exit.