Debugging FizzillaMachby Mike Pinkerton |
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.
Topics:
- Creating the project
- Setting breakpoints
- Looking at variables
- Debugging w/out ProjectBuilder
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!
- 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.
- 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, etc) in mozilla/dist. Do not click Finish!
- 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. 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. - 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. - 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 Ben 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
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.(gdb) p variable_name
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:
You can do this automatically, by default, using this command at the beginning of your debugging session:(gdb) x/wa *(void**) parent.mRawPtr
Once you've done that, using the 1st example, you can just type:(gdb) set print object on
and get a full expanded view of the actual, derived type of the object.(gdb) p *parent
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 (pinkerton@netscape.com)