Debugging on Mac OS X

  • Revision slug: Debugging_on_Mac_OS_X
  • Revision title: Debugging on Mac OS X
  • Revision id: 55316
  • Created:
  • Creator: Bhr
  • Is current revision? No
  • Comment /* Creating the Project in XCode */

Revision Content

So you've got it all built, and now you want to fix that bug that's been annoying you for so long. Using gdb from the Terminal app is fun, but Apple ships a nice UI for gdb with ProjectBuilder. This page describes how to set up a project for the mach-o build and use ProjectBuilder to debug it.

I'm assuming that you have the Mach-o version built, but if not, see the FizzillaMach homepage for more information. Be sure you have a debug build (ie, you don't have --disable-debug set) otherwise you won't be able to debug.

Special thanks to Zach Lipton for figuring out how to work with the legacy makefile project.


Creating the Project in ProjectBuilder

There's really not too much involved in this. Basically, we're creating a new project with a single target that's based on a legacy makefile.

  1. Create a new, empty project in ProjectBuilder.
  2. Choose New Target from the Project menu. Call your target "Debug", or whatever suits your fancy. The type of target you want is Legacy Makefile.
  3. Select the Targets tab on the left of the project window and click on the newly created target. The window should now be displaying the target customization panel.
  4. With the Custom Build Command tab selected, set the Build Tool to an empty text field. This ensures that ProjectBuilder wont try to do anything every time we tell it to run or debug.
  5. Select the Executables tab. Click the Add button and pick the MozillaDebug.app binary in mozilla/dist.
  6. To debug, click the Run and Debug button on the ProjectBuilder toolbar. ProjectBuilder will launch mozilla and dump all its output to its internal console.


Creating the Project in XCode

XCode is Apple's new set of developer tools and IDE, shipping with MacOS X 10.3 Panther. The instructions for creating a project to debug Mozilla with XCode are slightly different from those for ProjectBuilder, and also slightly different than those given by Apple itself for debugging arbitrary executables!

  1. Open XCode, and create a new Project with **File, New Project**. Select **Empty Project** as the project type, click Next, then name the project and choose a location for it, then click Finish.
  2. Now you need to add the executable. Select **Project, Add Custom Executable** and type a pretty name, then use the **Choose** to locate the .app file (Mozilla.app, Firefox.app, DeerParkDebug.app etc) in mozilla/dist. Do not click Finish!
  3. **IMPORTANT** - XCode will dim all UI that lets you run the program in the debugger if you just specify the .app file. You must manually locate the Mozilla executable inside the executable package and edit the path to point to that location. Thus the value of the Executable Path field might look like this: " ~/builds/mozilla/dist/Mozilla.app/Contents/MacOS/Mozilla-bin" (Make sure to select the "-bin" file, the non-"-bin" is just a launch script.) Now click **Finish** (on xcode 1.2 and higher, just close the dialog). 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.
  4. 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.
  5. To start the debugger, you have to use **Debug, Debug Executable** as for some reason the toolbar debug button will remain disabled.

Happy XCode debugging! (added by [Goodger] on 10/25/2003)

Building From Within ProjectBuilder

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.

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 Without ProjectBuilder

Sure, it's cool, but it's extra overhead that those who love command lines and the Terminal app might not care for. In order to debug from the command line, you need to do the following.

1. Cd down into the Mozilla package in dist

% cd dist/MozillaDebug.app/Contents/MacOS 

2. Set the DYLD_LIBRARY_PATH var to tell the loader that this is where all the shared libraries are

% setenv DYLD_LIBRARY_PATH `pwd` 

3. Debug

% cd ../../.. 
% gdb ./mozilla-bin 

What If It Doesn't Work?

There are two places to go for help building Fizzilla:

  • the #mozilla channel at irc.mozilla.org is a good place to find some mac weenies in real time who, if you ask nicely and don't be a pest, will happily get you on your way
  • post to the newsgroup netscape.public.mozilla.macosx

written by Mike Pinkerton (), maintained and migrated by Brian Ray ()

Revision Source

<p>So you've got it all built, and now you want to fix that bug that's been annoying you for so long. Using gdb from the Terminal app is fun, but Apple ships a nice UI for gdb with ProjectBuilder. This page describes how to set up a project for the mach-o build and use ProjectBuilder to debug it.
</p><p>I'm assuming that you have the Mach-o version built, but if not, see the <a class="external" href="http://www.mozilla.org/ports/fizzilla/Mach.html">FizzillaMach homepage</a> for more information. Be sure you have a debug build (ie, you don't have --disable-debug set) otherwise you won't be able to debug.
</p><p>Special thanks to Zach Lipton for figuring out how to work with the legacy makefile project.
</p>
<hr>
<h3 name="Creating_the_Project_in_ProjectBuilder"> Creating the Project in ProjectBuilder </h3>
<p>There's really not too much involved in this. Basically, we're creating a new project with a single target that's based on a legacy makefile.
</p>
<ol><li> Create a new, empty project in ProjectBuilder. 
</li><li> Choose <b>New Target</b> from the <b>Project</b> menu. Call your target "Debug", or whatever suits your fancy. The type of target you want is <b>Legacy Makefile</b>. 
</li><li> Select the <b>Targets</b> tab on the left of the project window and click on the newly created 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 an empty text field. This ensures that ProjectBuilder wont try to do anything every time we tell it to run or debug. 
</li><li> Select the <b>Executables</b> tab. Click the <b>Add</b> button and pick the MozillaDebug.app binary in mozilla/dist. 
</li><li> To debug, click the <b>Run and Debug</b> button on the ProjectBuilder toolbar. ProjectBuilder will launch mozilla and dump all its output to its internal console. 
</li></ol>
<p><br>
</p>
<h3 name="Creating_the_Project_in_XCode"> Creating the Project in XCode </h3>
<p>XCode is Apple's new set of developer tools and IDE, shipping with MacOS X 10.3 Panther. The instructions for creating a project to debug Mozilla with XCode are slightly different from those for ProjectBuilder, and also slightly different than those given by Apple itself for debugging arbitrary executables!
</p>
<ol><li> Open XCode, and create a new Project with **File, New Project**. Select **Empty Project** as the project type, click Next, then name the project and choose a location for it, then click Finish.
</li><li> Now you need to add the executable. Select **Project, Add Custom Executable** and type a pretty name, then use the **Choose** to locate the .app file (Mozilla.app, Firefox.app, DeerParkDebug.app etc) in mozilla/dist. Do not click Finish!
</li><li> **IMPORTANT** - XCode will dim all UI that lets you run the program in the debugger if you just specify the .app file. You must manually locate the Mozilla executable inside the executable package and edit the path to point to that location. Thus the value of the Executable Path field might look like this:  " ~/builds/mozilla/dist/Mozilla.app/Contents/MacOS/Mozilla-bin" (Make sure to select the "-bin" file, the non-"-bin" is just a launch script.) Now click **Finish** (on xcode 1.2 and higher, just close the dialog). 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> 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.
</li><li> To start the debugger, you have to use **Debug, Debug Executable** as for some reason the toolbar debug button will remain disabled.
</li></ol>
<p>Happy XCode debugging! (added by [<a class="external" href="mailto:ben@mozilla.org|Ben">Goodger</a>] on 10/25/2003)
</p>
<h3 name="Building_From_Within_ProjectBuilder"> Building From Within ProjectBuilder </h3>
<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="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_Without_ProjectBuilder"> Debugging Without ProjectBuilder </h3>
<p>Sure, it's cool, but it's extra overhead that those who love command lines and the Terminal app might not care for. In order to debug from the command line, you need to do the following.
</p><p>1. Cd down into the Mozilla package in dist 
</p>
<pre class="eval">% cd dist/MozillaDebug.app/Contents/MacOS 
</pre>
<p>2. Set the DYLD_LIBRARY_PATH var to tell the loader that this is where all the shared libraries are 
</p>
<pre class="eval">% setenv DYLD_LIBRARY_PATH `pwd` 
</pre>
<p>3. Debug 
</p>
<pre class="eval">% cd ../../.. 
% gdb ./mozilla-bin 
</pre>
<h3 name="What_If_It_Doesn.27t_Work.3F"> What If It Doesn't Work? </h3>
<p>There are two places to go for help building Fizzilla:
</p>
<ul><li> the #mozilla channel at irc.mozilla.org is a good place to find some mac weenies in real time who, if you ask nicely and don't be a pest, will happily get you on your way
</li><li> post to the newsgroup netscape.public.mozilla.macosx
</li></ul>
<hr>
<p>written 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">)
</a></p>
Revert to this revision