This document explains how to debug Mozilla-derived applications such as Firefox, Thunderbird and Seamonkey on Mac OS X using Xcode.
(Some old documentation that has not been migrated to Xcode is also available, on how to build from within Project Builder)
See the Mac OS X build requirements.
Creating the project in Xcode
Xcode is Apple's set of developer tools and IDE, shipping with the system starting with MacOS X 10.3.
- Open Xcode, and create a new Project with File > New Project. In the list, choose "Empty Project" as the project type, click Next, name the project and choose where to save the project, then click Finish.
- Now you need to add the executable. Select Project > Add Custom Executable and type a pretty name, then click the Choose button to locate the .app file that you want to debug (Mozilla.app, Firefox.app, DeerParkDebug.app etc). The .app file is typically found inside the "dist" folder in your build directory. Do not click Finish yet!
- Important! - Xcode won't make your life that easy; there is still one vital step to be done. You must manually locate the executable binary inside the application bundle, and edit the path to that. In other words, the value of the Executable Path textfield should look something like this: " ~/builds/mozilla/dist/DeerParkDebug.app/Contents/MacOS/firefox-bin" (Make sure to select the "-bin" file, the non-"-bin" is just a launch script.)
- Now click Finish. An entry with the pretty name you entered in the previous dialog will appear under the "Executables" branch of the "Groups & Files" pane in the Xcode Project window.
Starting a debug session
When you have created a project file, simply use the menuitem Debug > Debug Executable, or change to the debug view by clicking the debug button (typically the third one from the left), and click "Debug".
gdb is quite good about finding the source files as long as you have a debug build. You can set breakpoints from Project Builder's console by typing:
(gdb) fb nsWindow::Scroll
You can also set a breakpoint visually, with Project Builder's UI. This is useful when the place you want to stop is in the startup sequence, before you have a chance to set a breakpoint from the console. Also, breakpoints set with the UI are persistent. Here's how to set the same breakpoint as was set above using the UI:
- Open mozilla/widget/src/mac/nsWindow.cpp in ProjectBuilder
- Select Add Current File To Project from the Project menu. You can also drag the file into the Files panel from the Finder.
- Find nsWindow::Scroll in the file and click just to the left of the first line in the routine.
Now when you click the down arrow on a webpage, ProjectBuilder will pop to the front and after a delay (5 seconds, maybe) the line where execution has halted is drawn in red. Notice that you can walk back up the call stack and look at the source even though you didn't add any of those files to the project. ProjectBuilder only needs the files for which you want to set breakpoints in the project.
Looking at variables
If you've used a gui debugger before (such as CodeWarrior), this is pretty simple. There are two things that should be pointed out, though.
First, when looking at variables that are C++ classes, ProjectBuilder separates the protected/private members into their own subhierarchy called protected and private, respectively. It will also group inherited member variables into a subhierarchy named by the parent class. If you don't see the member variable you're looking for, make sure that you drill down all the way.
If you want to save yourself the hassle of flipping down many little triangles, you can display a variable on the console using
(gdb) p variable_name
Pointer types aren't automatically dereferenced so you may need to do that in the command. It will write out a fully expanded representation to the console. This representation is extremely customizable with various set print feature-name commands. See the gdb doc.
The other issue is how to see the data through a COM interface pointer. As we all know, CodeWarrior had issues with this, but gdb is able with a little coaxing. This reason alone might be enough to make a lot of people switch to the mach-o builds!
For example, if you have a variable
and you want to see what's behind the interface pointer, do the following command in the gdb console panel
(gdb) x/wa *(void**) parent
and gdb will respond with
0x31fb06c <__vt_13nsChildWindow>: 0x0
This tells us that the implementation class for this particular interface pointer is nsChildWindow. To display the variable, we have to cast it to an nsChildWindow* when we print:
The trick to seeing into nsCOMPtrs is the same. The only difference is that you have to look at the mRawPtr member of the nsCOMPtr. Assuming you have
The above x command would become:
(gdb) x/wa *(void**) parent.mRawPtr
You can do this automatically, by default, using this command at the beginning of your debugging session:
(gdb) set print object on
Once you've done that, using the 1st example, you can just type:
(gdb) p *parent
and get a full expanded view of the actual, derived type of the object.
Debugging from the Terminal with gdb
Sure, it's cool to use a GUI, but we all know that real hackers prefer the Terminal. In order to debug from the command line, you need to do the following.
1. Cd into your Mozilla application (.app) bundle. As an example, we'll use a Firefox debug build.
% cd dist/DeerParkDebug.app/Contents/MacOS
2. Set the DYLD_LIBRARY_PATH environment variable to tell the loader that this is where all the shared libraries are
% export DYLD_LIBRARY_PATH=pwd
% cd ../../.. % gdb ./firefox-bin
Building from within Project Builder
Please note! Project Builder is no longer used for building. This part of the documentation is still to be migrated to Xcode. As of writing this, at least Xcode 1.5 is required for building the Mozilla source tree. Please see the build requirements for more information on this.
The executable of the project is the MozillaDebug.app bundle. The bundle needs to be updated whenever you rebuild a component or change a chrome file. This used to be horribly slow because the entire contents (over 400 MB!) were copied each time.
Fortunately, that bundling process has been improved to only update what's changed - making it convenient to set up the "Build" of the project to update the bundle. You can then build components or modify chome, return to Project Builder, choose "Build and Debug," and be off and running with your changes. Here's how to set it up:
- Select the Targets tab on the left of the project window and click on your target. The window should now be displaying the target customization panel.
- With the Custom Build Command tab selected, set the Build Tool to >/usr/bin/gnumake
- Set the Arguments to: >$ACTION Project Builder will pass an argument in $ACTION so that "Clean" works, in addition to "Build."
- Click Set... to set the Directory field and pick the xpfe/bootstrap directory in your mozilla tree. When you're done, it should read something like: >/usr/pink/src/mozilla/xpfe/bootstrap
Now clicking any of the Build or Run or Debug buttons on the ProjectBuilder toolbar will update MozillaDebug.app and show any messages or errors in the ProjectBuilder window.
Having a profile for debugging purposes
I highly recommend you create a separate profile to debug with, whatever your task, so that you don't lose precious data like Bookmarks, saved passwords, etc. So that you're not bothered with the profile manager every time you start to debug, expand the "Executables" branch of the "Groups & Files" list and double click on the Executable you added for Mozilla. Click the plus icon under the "Arguments" list and type "-P <profile name>" (e.g. "-P MozillaDebug"). Close the window when you're done.
- Try asking nicely in our IRC channel #developers. Often many mac developers usually hang out in the #camino channel.
Originally version by Mike Pinkerton (), maintained and migrated by Brian Ray (). Updated to the new Xcode world by Håkan Waara ().