Automatically save the dSYM files.
For those not aware, when you compile an Objective-C application, whether it be for the desktop or for Cocoa Touch devices, the debugging symbols are stripped out of the binaries. Therefore, unlike other languages such as Java, when a crash occurs, there is virtually no way to determine where the crash occurred. However, when the applications are compiled, a dSYM bundle is generated. This bundle allows us to match up the debugging symbols with the application’s crash log to help determine the cause of the crash.
The issue, however, is that the dSYM file must match the binary exactly[1]. I have not even had luck with pulling the code out of version control and compiling a second time to get the files to match up. Therefore, we need to store these dSYM files for every build that gets handed off to someone else. For Cocoa Touch development this means every ad hoc build and every release build. This can be a pain.
To solve this problem I wrote a script that is added as the last build phase of all my iPhone projects. The script will move the dSYM bundle into the project directory in a directory cleverly called “dSYM”. In addition the script will check the bundle into git (after confirming the project is maintained in a git repository) and commit just that bundle. Also, since the file is always named the same thing, it renames the file using the current date and time so that no two bundles have the same file name.
Failure checks
The first thing the script does is determine if it should run.
if [ "$BUILD_STYLE" == "Debug" ]; then
echo "Skipping debug"
exit 0;
fi
The first part of the script checks to see if the build style is Debug. Since all debug builds run just on the developer’s device and still contain the debug symbols, they can be safely ignored.
if [ "$EFFECTIVE_PLATFORM_NAME" == "-iphonesimulator" ]; then
echo "Skipping simulator build"
exit 0;
fi
The second step is to check to see if the build is against the simulator. Again, we have no interest in storing the symbol files for these builds.
Move the file
Since the location of the file is determined at build time and can vary from developer to developer (as well as machine to machine) I use the environmental variables that are part of the build.
SRC_PATH=${DWARF_DSYM_FOLDER_PATH}/${DWARF_DSYM_FILE_NAME}
RELATIVE_DEST_PATH=dSYM/${EXECUTABLE_NAME}.$(date +%Y%m%d%H%M%S).app.dSYM
DEST_PATH=${PROJECT_DIR}/${RELATIVE_DEST_PATH}
echo "moving ${SRC_PATH} to ${DEST_PATH}"
mv "${SRC_PATH}" "${DEST_PATH}"
The next step is to build up the paths of where the file is currently and where we are going to move it to. I like to store these in variables so that I can print them out to the console in case something goes wrong.
Commit it to version control
Every project is part of version control right? (RIGHT?)
if [ -f ".git/config" ]; then
git add "${RELATIVE_DEST_PATH}"
git commit -m "Added dSYM file for ${BUILD_STYLE} build" \
"${RELATIVE_DEST_PATH}"
fi
The final part only occurs if the project is part of a git repository. If it is then the bundle is added to git and then just that bundle is committed with a simple message.
Conclusion
Although I have recently been having issues getting symbolicatecrash
to work properly, eventually either I or someone else will get it working again and at that time these files will be invaluable in tracking down crashes.
The final script is attached. I normally add the file to the project and then have a script phase that just calls this script file.
[1]Mach-o objects have an embedded uuid which must match the uuid of the dsym files. That’s why recompiling doesn’t work. —Graham Lee
Update
Reader Rob Elkin wrote in to give us this tip:
Here is the updated script I’m now using:
echo "starting dsym archiving" if [ "$BUILD_STYLE" == "Debug" ]; then echo "Skipping debug" exit 0; fi if [ "$EFFECTIVE_PLATFORM_NAME" == "-iphonesimulator" ]; then echo "Skipping simulator build" exit 0; fi SRC_PATH=${ARCHIVE_DSYMS_PATH}/${DWARF_DSYM_FILE_NAME} RELATIVE_DEST_PATH=dSYM/${EXECUTABLE_NAME}.$(date +%Y%m%d%H%M%S).app.dSYM DEST_PATH=${PROJECT_DIR}/${RELATIVE_DEST_PATH} echo "moving ${SRC_PATH} to ${DEST_PATH}" cp -r "${SRC_PATH}" "${DEST_PATH}" if [ -f ".git/config" ]; then git add "${RELATIVE_DEST_PATH}" git commit -m "Added dSYM file for ${BUILD_STYLE} build" "${RELATIVE_DEST_PATH}" fi Basically the important change was: SRC_PATH=${ARCHIVE_DSYMS_PATH}/${DWARF_DSYM_FILE_NAME}
Because now the path to the archive is in a different place (specifically ~/Library/Developer/Xcode/Archives/
/ I also changed the mv to a cp -r, as my preference is to copy the file so there is never a chance it could get lost.
Hope this helps.
Indeed it does Rob; thank you for sharing your changes.
Thank you for your script. I’ll definitly use it in my projects.
But IMO
if [ -f “.git/config” ]; then
git add “${RELATIVE_DEST_PATH}”
git commit -m “Added dSYM file for ${BUILD_STYLE} build” \
“${RELATIVE_DEST_PATH}”
fi
is a bit dangerous. What if I’ve got uncommited changes which are already in the index (so to speak, I already git add’ed them)? They will be commited, too, aren’t they? Any kind of solution for that?
The git commit command narrows the commit to only those files included in the dSYM. Even if you have other files “added” it will not commit them because of the way the git commit line is structured. Therefore it is completely “safe”.
[…] Marcus Zarra, who is a very well-known Cocoa developer, explains the process over on his blog Cocoa Is My Girlfriend and even provides a set of scripts you can customize to get this set up in your own development […]