Debugging on Mac OS X

  • Revision slug: Debugging_on_Mac_OS_X
  • Revision title: Debugging on Mac OS X
  • Revision id: 55329
  • Created:
  • Creator: Hakanw
  • Is current revision? No
  • Comment adding a new step that after much trial and error turns out to be important to get debugging working (especially if you want to debug stuff during startup)

Revision Content

This document explains how to debug Mozilla-derived applications such as Firefox, Thunderbird and Seamonkey on Mac OS X using Xcode. For specific information on a way to debug hangs, see Debugging a hang on OS X.

(Some old documentation that has not been migrated to Xcode is also available, on how to build from within the IDE prior to Xcode, Project Builder)


Requirements

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 as of Mac OS X 10.3.

  1. 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.
  2. Now you need to add the executable. Select Project > New 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!
  3. 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.)
  4. 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.
  5. In order for Xcode to know what sources you want to debug, you need to tell it where to find them. Go to Project > Edit Active Executable, click the Debugging tab, and finally drag your top-level mozilla/ directory to the panel "Additional folders to find source files in".
  6. Go to Xcode > Preferences. Find the Debugging preferences pane, and make sure "Load symbols lazily" is unchecked.
  7. Finally, if you are debugging Firefox, Thunderbird or some other application that supports multiple profiles, using a separate profile for debugging purposes is recommended. See "Having a profile for debugging purposes" below.

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".

Setting breakpoints

Setting a breakpoint is easy. Just open the source file you want to debug in Xcode, and click in the margin to the left of the line of code where you want to break.

During the debugging session, each time that line is executed, the debugger will break there, and you will be able to debug it.

Having a profile for debugging purposes

It is recommended to 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.

Looking at variables

Printing out information in realtime about "native" Objective-C objects is simple. For example, if there's an NSString named 'myString' that you want to get the contents of, you can use the print-object (or short: po) command.

(gdb) po myString
'This is the contents of the string'

Below are instructions for how to deal with C++ classes, such as when debugging code written in XPCOM.

If you've used a debugger before, this is pretty simple. There are two things that should be pointed out, though.

First, when looking at variables that are C++ classes, Xcode 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.

The simplest way to do this is to add

set print object on

to your ~/.gdbinit file. If you don't want to do that for some reason, you can type that in the gdb console panel in XCode (or at the gdb prompt if you're debugging from the command line) at the beginning of your debugging session.

If you don't like the idea of gdb determining the types of interface pointers by default, you can do it manually.

For example, if you have a variable

nsIWidget* parent;

and you're using "set print object on" you can just do

p *parent

to have gdb determine the concrete class and list its members. If you're doing things by hand 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:

p *(nsChildWindow*)parent

Note that you have to be a little careful with the casting if the concrete class has multiple inheritance from the interface in question (particularly a problem with nsISupports). In that case, you have to cast through the correct superclass; figuring out which one might take some work.

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

nsCOMPtr<nsIWidget> parent;

The above x command would become:

(gdb) x/wa *(void**) parent.mRawPtr

Debugging from the Terminal with gdb

Debugging from within Xcode (see above) is much easier, but debugging with gdb in the Terminal is also possible.

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`

3. Debug

% 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:

  1. 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.
  2. With the Custom Build Command tab selected, set the Build Tool to >/usr/bin/gnumake
  3. Set the Arguments to: >$ACTION Project Builder will pass an argument in $ACTION so that "Clean" works, in addition to "Build."
  4. 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.

Questions? Problems?

  • Try asking nicely in our IRC channel #developers. Often many mac developers usually hang out in the #camino channel.

Original version by Mike Pinkerton (), maintained and migrated by Brian Ray (). Updated to the new Xcode world by Håkan Waara ().

Revision Source

<p>This document explains how to debug Mozilla-derived applications such as Firefox, Thunderbird and Seamonkey on Mac OS X using Xcode. For specific information on a way to debug hangs, see <a href="en/Debugging_a_hang_on_OS_X">Debugging a hang on OS X</a>.
</p><p>(Some old documentation that has not been migrated to Xcode is also available, on how to build from within the IDE prior to Xcode, Project Builder)
</p>
<hr>
<p><s id="req"></s>
</p>
<h3 name="Requirements"> Requirements </h3>
<p>See the <a href="en/Mac_OS_X_Build_Prerequisites">Mac OS X build requirements</a>.
</p>
<h3 name="Creating_the_project_in_Xcode"> Creating the project in Xcode </h3>
<p>Xcode is Apple's set of developer tools and IDE, shipping with the system as of Mac OS X 10.3.
</p>
<ol><li> Open Xcode, and create a new Project with File &gt; 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.
</li><li> Now you need to add the executable. Select Project &gt; New 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!
</li><li> <b>Important!</b> - Xcode won't make your life <i>that</i> 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.) 
</li><li> Now click Finish. An entry with the pretty name you entered in the previous dialog will appear under the "Executables" branch of the "Groups &amp; Files" pane in the Xcode Project window.
</li><li> In order for Xcode to know what sources you want to debug, you need to tell it where to find them. Go to Project &gt; Edit Active Executable, click the Debugging tab, and finally drag your top-level mozilla/ directory to the panel "Additional folders to find source files in".
</li><li> Go to Xcode &gt; Preferences. Find the Debugging preferences pane, and make sure "Load symbols lazily" is <i>unchecked</i>.
</li><li> Finally, if you are debugging Firefox, Thunderbird or some other application that supports multiple profiles, using a separate profile for debugging purposes is recommended. See "Having a profile for debugging purposes" below.
</li></ol>
<h3 name="Starting_a_debug_session"> Starting a debug session </h3>
<p>When you have created a project file, simply use the menuitem Debug &gt; Debug Executable, or change to the debug view by clicking the debug button (typically the third one from the left), and click "Debug".
</p>
<h3 name="Setting_breakpoints"> Setting breakpoints </h3>
<p>Setting a breakpoint is easy. Just open the source file you want to debug in Xcode, and click in the margin to the left of the line of code where you want to break.
</p><p>During the debugging session, each time that line is executed, the debugger will break there, and you will be able to debug it.
</p>
<h3 name="Having_a_profile_for_debugging_purposes"> Having a profile for debugging purposes </h3>
<p>It is recommended to 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 &amp; Files" list and double click on the Executable you added for Mozilla. Click the plus icon under the "Arguments" list and type "-P &lt;profile name&gt;" (e.g. "-P MozillaDebug"). Close the window when you're done.
</p>
<h3 name="Looking_at_variables"> Looking at variables </h3>
<p>Printing out information in realtime about "native" Objective-C objects is simple. For example, if there's an NSString named 'myString' that you want to get the contents of, you can use the print-object (or short: po) command.
</p>
<pre class="eval">(gdb) po myString
'This is the contents of the string'
</pre>
<p>Below are instructions for how to deal with C++ classes, such as when debugging code written in XPCOM.
</p><p>If you've used a debugger before, this is pretty simple. There are two things that should be pointed out, though.
</p><p>First, when looking at variables that are C++ classes, Xcode separates the protected/private members into their own subhierarchy called <b>protected</b> and <b>private</b>, 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.
</p><p>If you want to save yourself the hassle of flipping down many little triangles, you can display a variable on the console using
</p>
<pre class="eval">(gdb) p variable_name
</pre>
<p>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 <b>set print feature-name</b> commands. See the gdb doc.
</p><p>The other issue is how to see the data through a COM interface pointer.
</p><p>The simplest way to do this is to add
</p>
<pre class="eval">set print object on
</pre>
<p>to your <code>~/.gdbinit</code> file.  If you don't want to do that for some reason, you can type that in the gdb console panel in XCode (or at the gdb prompt if you're debugging from the command line) at the beginning of your debugging session.
</p><p>If you don't like the idea of gdb determining the types of interface pointers by default, you can do it manually.
</p><p>For example, if you have a variable
</p>
<pre class="eval">nsIWidget* parent;
</pre>
<p>and you're using "<code>set print object on</code>" you can just do
</p>
<pre class="eval">p *parent
</pre>
<p>to have gdb determine the concrete class and list its members.  If you're doing things by hand and you want to see what's behind the interface pointer, do the following command in the gdb console panel
</p>
<pre class="eval">(gdb) x/wa *(void**) parent
</pre>
<p>and gdb will respond with
</p>
<pre class="eval">0x31fb06c &lt;__vt_13nsChildWindow&gt;: 0x0
</pre>
<p>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:
</p>
<pre class="eval">p *(nsChildWindow*)parent
</pre>
<p>Note that you have to be a little careful with the casting if the concrete class has multiple inheritance from the interface in question (particularly a problem with nsISupports).  In that case, you have to cast through the correct superclass; figuring out which one might take some work.
</p><p>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
</p>
<pre class="eval">nsCOMPtr&lt;nsIWidget&gt; parent;
</pre>
<p>The above x command would become:
</p>
<pre class="eval">(gdb) x/wa *(void**) parent.mRawPtr
</pre>
<h3 name="Debugging_from_the_Terminal_with_gdb"> Debugging from the Terminal with gdb </h3>
<p>Debugging from within Xcode (see above) is much easier, but debugging with gdb in the Terminal is also possible.
</p><p>1. Cd into your Mozilla application (.app) bundle. As an example, we'll use a Firefox debug build.
</p>
<pre class="eval">% cd dist/DeerParkDebug.app/Contents/MacOS 
</pre>
<p>2. Set the DYLD_LIBRARY_PATH environment variable to tell the loader that this is where all the shared libraries are 
</p>
<pre class="eval">% export DYLD_LIBRARY_PATH=`pwd`
</pre>
<p>3. Debug 
</p>
<pre class="eval">% cd ../../.. 
% gdb ./firefox-bin
</pre>
<h3 name="Building_from_within_Project_Builder"> Building from within Project Builder </h3>
<p><b>Please note!</b> Project Builder is no longer used for building. This part of the documentation is still to be migrated to Xcode. As of writing this, <i>at least Xcode 1.5</i> is required for building the Mozilla source tree. Please see the <a href="#req">build requirements</a> for more information on this.
</p><p>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.
</p><p>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:
</p>
<ol><li> Select the <b>Targets</b> tab on the left of the project window and click on your target. The window should now be displaying the target customization panel. 
</li><li> With the <b>Custom Build Command</b> tab selected, set the <b>Build Tool</b> to &gt;/usr/bin/gnumake 
</li><li> Set the Arguments to: &gt;$ACTION Project Builder will pass an argument in $ACTION so that "Clean" works, in addition to "Build." 
</li><li> Click <b>Set...</b> to set the <b>Directory</b> field and pick the xpfe/bootstrap directory in your mozilla tree. When you're done, it should read something like: &gt;/usr/pink/src/mozilla/xpfe/bootstrap 
</li></ol>
<p>Now clicking any of the <b>Build</b> or <b>Run</b> or <b>Debug</b> buttons on the ProjectBuilder toolbar will update MozillaDebug.app and show any messages or errors in the ProjectBuilder window.
</p>
<h3 name="Questions.3F_Problems.3F"> Questions? Problems? </h3>
<ul><li> Try asking nicely in our IRC channel <a class="external" href="irc://irc.mozilla.org/developers">#developers</a>. Often many mac developers usually hang out in the <a class="external" href="irc://irc.mozilla.org/camino">#camino</a> channel.
</li></ul>
<hr>
<p>Original version by Mike Pinkerton (<a class="external" href="mailto:pinkerton@netscape.com">), maintained and migrated by Brian Ray (</a><a class="external" href="mailto:bray@sent.com">).  Updated to the new Xcode world by Håkan Waara (</a><a class="external" href="mailto:hwaara@gmail.com">).
</a></p>
Revert to this revision