Debugging on Mac OS X

  • Revision slug: Debugging_on_Mac_OS_X
  • Revision title: Debugging on Mac OS X
  • Revision id: 55320
  • Created:
  • Creator: Hakanw
  • Is current revision? No
  • Comment Debugging FizzillaMach moved to Debugging on Mac OS X

Revision Content

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)


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 starting with MacOS 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 > 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!
  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.

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

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:

  1. Open mozilla/widget/src/mac/nsWindow.cpp in ProjectBuilder
  2. Select Add Current File To Project from the Project menu. You can also drag the file into the Files panel from the Finder.
  3. 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

nsIWidget* parent;

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

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

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 

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.

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.

Questions? Problems?

  • 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 ().

Revision Source

<p>This document explains how to debug Mozilla-derived applications such as Firefox, Thunderbird and Seamonkey on Mac OS X using Xcode.
</p><p>(Some old documentation that has not been migrated to Xcode is also available, on how to build from within 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 starting with MacOS 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; 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!
</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></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>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:
</p>
<pre class="eval">(gdb) fb nsWindow::Scroll
</pre>
<p>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:
</p>
<ol><li> Open mozilla/widget/src/mac/nsWindow.cpp in ProjectBuilder 
</li><li> Select <b>Add Current File To Project</b> from the <b>Project</b> menu. You can also drag the file into the Files panel from the Finder. 
</li><li> Find nsWindow::Scroll in the file and click just to the left of the first line in the routine. 
</li></ol>
<p>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.
</p>
<h3 name="Looking_at_variables"> Looking at variables </h3>
<p>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.
</p><p>First, when looking at variables that are C++ classes, ProjectBuilder 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. 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!
</p><p>For example, if you have a variable
</p>
<pre class="eval">nsIWidget* parent;
</pre>
<p>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>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>
<p>You can do this automatically, by default, using this command at the beginning of your debugging session:
</p>
<pre class="eval">(gdb) set print object on
</pre>
<p>Once you've done that, using the 1st example, you can just type:
</p>
<pre class="eval">(gdb) p *parent
</pre>
<p>and get a full expanded view of the actual, derived type of the object.
</p>
<h3 name="Debugging_from_the_Terminal_with_gdb"> Debugging from the Terminal with gdb </h3>
<p>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.
</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="Having_a_profile_for_debugging_purposes"> Having a profile for debugging purposes </h3>
<p>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 &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="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://mozilla.org/developers">#camino</a> channel.
</li></ul>
<hr>
<p>Originally 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