dSYM in your bundle or just happy to see me

It’s been a while since I posted last. Rest assured that I did survive the Y2K9 disaster, though not unscathed. Since bloggers and other entertainers — such as Brian Williams — are required by law to offer a retrospective at the end of a year, I’ve been scanning the Top 10 lists of Top 10 lists of things that we have gained and lost in 2008. Next to our collective sanity, the most significant loss of the year was STABS. Actually, it wasn’t so much lost as deprecated. This means that we can’t expect any new features (or bugs!), and support for STABS debugging symbols may disappear in some future operating system, say, Windows -400. (I assume that the countdown of version numbers from 95 to 7 is intended to accurately represent the software’s regression.) In the transition from STABS to DWARF, it was thought (by the people who matter, viz., me) that we also lost the ability to ship debugging symbols with our apps. Luckily, it was discovered (again, by the people who matter) that we did not lose this ability.

Developers sometimes need to give users a debug version of an application. For example, a user may be experiencing an exception or crash that the developer cannot reproduce. Including debugging symbols with the app allows the reports to be fully symbolized. With STABS, the symbols reside within the app’s executable, so shipping them is trivial. The DWARF with dSYM format, on the other hand, puts the debugging symbols in a separate file. (To be accurate, a separate file within a separate bundle, but we’ll ignore that fact for this sentence.) By default, Xcode creates MyApp.app.dSYM in the same folder as MyApp.app, and indeed, Leopard’s crash reporter can locate MyApp.app.dSYM in the same folder as MyApp.app regardless of which folder they’re in on disk. Theoretically, then, you could have the user put a .dSYM in the same folder as the app. However, making the user do this would be, in a word, lame. In two words, pretty lame. Moreover, it doesn’t work at all on Tiger. Pretty, pretty lame.

When I face an insoluble problem, my tendency is to step back and get philosophical. Why do I exist? Why does the universe hate me? Who was the real Darrin? More to the point: what is an app? Essentially, an app is a command-line tool in a box with a pretty bow. (Another iSweater, just what I needed!) An app’s main executable file is located in the directory Contents/MacOS of the .app bundle. You can even launch an app from the command line, e.g.,

/Applications/Safari.app/Contents/MacOS/Safari

assuming that you haven’t deleted Safari for security reasons. So how does this information help us? It doesn’t — I’m just killing time here. However, it’s worth noting that if you build the Release configuration of a command-line tool project, Xcode by default creates MyTool.dSYM in the same folder as MyTool. In both Leopard and Tiger, the crash reporter can locate the .dSYM there. Thus, you would expect that the crash reporter can also locate MyApp.app/Contents/MacOS/MyApp.dSYM when your app crashes. And you would be right! (Of course, you would expect this because I just told you, whereas originally you would have expected to try a bunch of stuff and fail, like putting MyApp.app.dSYM in MyApp.app/Contents/MacOS.)

The beauty of this technique is that it works not only for the app’s main executable but also for other embedded executables such dynamic libraries and frameworks. When a crash occurs involving MyFramework.framework, the crash reporter will find

MyApp.app/Contents/Frameworks/MyFramework.framework/Versions/A/MyFramework.dSYM

You can build the framework in a separate Xcode project and copy the product along with its embedded dSYM into your app’s bundle, and the symbols will be found at crashtime. (That’s runtime with a bang.) In Tiger, the line numbers of the source code files can sometimes be a little off in the crash reports; this may be due to bugs in the handling of stripped binaries by atos, which I mentioned in my earlier post.

Now that we know where to put the debugging symbols in the app bundle, how do we get them there? Manual copying is unthinkable (like giving David Pogue a copy of OS X GM before ADC members, or putting Leon Panetta in charge of the CIA). If your entire build process is not automated, you should give up software development immediately and look for another career; I recommend professional ice dancing. You could write a shell script to copy MyApp.app.dSYM from the build directory, but that’s only slightly less annoying than having your users copy it to /Applications, because it’s something that Xcode should do itself.

Fortunately, the Xcode build setting reference tells us how to configure this. Or so one would think. Well, at least the relevant build settings are found in the environment variables … after you’ve written your shell script. The Xcode build transcript normally doesn’t show environment variables, but you can add a run script build phase to your target and check the option “Show environment variables in build log”. The environment variables reveal the default values for DWARF_DSYM_FOLDER_PATH and DWARF_DSYM_FILE_NAME, which Xcode uses in creating the dSYM file. Although you won’t find them in the target’s list of build settings, you can create them yourself in the User-Defined section. To embed the dSYM within the app bundle, just set DWARF_DSYM_FOLDER_PATH to $(CONFIGURATION_BUILD_DIR)/$(EXECUTABLE_FOLDER_PATH) and DWARF_DSYM_FILE_NAME to $(EXECUTABLE_NAME).dSYM. These settings should work for both apps and frameworks.

My beard has grown longer over the course of this post, and my knees are starting to ache, so it’s time to wrap it up, tip my hat to the new year, and meet the new boss.

3 Responses to “dSYM in your bundle or just happy to see me”

  1. eric says:

    Geez this was hard to follow, did the cats write it?

  2. Rob Keniger says:

    Doesn’t shipping the symbols file with the app defeat the whole purpose of stripping the symbols in the first place?

  3. Jeff says:

    Eric, yes. They work for food.

    Rob, it depends on what you mean. On the logistical side, this is to be done for debugging only, not for the ‘GM’ of the app. On the technical side, stripping has very different implications for DWARF than for STABS. As I discussed in my earlier post, DWARF symbols are not put in the executable itself. The executable only contains references to the intermediate object files. Thus, stripping merely removes those references, not the debugging symbols, which were never there to be stripped.