Debugging Mozilla with gdb

This page details some tricks that you can use to make it easier to debug Mozilla and to work around some of the problems that GDB has.

Make sure you have gdb 5 or higher.  You can get a more recent copy of GDB from the GDB site at sourceware.  If you have less than 256 MB of RAM, be sure to see Using gdb on wimpy computers.

Where can I find general gdb documentation?

Using GDB is well beyond the scope of this document. There is documentation available on your system if you have GDB installed in the form of info pages. You might want to use the gnome help browser to read that documentation as many people find the info reader on Linux hard to use. Additionally, you can use a graphical front end to GDB like ddd or insight. Here's a site that has more information:

How do I run Mozilla under gdb?

The preferred way is to use the mach command-line tool to help run the debugger.  It takes care of passing several useful defaults that you can optionally disable.  Use "mach help debug" to get more details.  If you are in the source directory, you would use "./mach".  If you have added mach to your path, then you can just use "mach".  (Note that mach is aware of mozconfigs.)

$ ./mach debug [arguments to pass to firefox]

If you would like to pass arguments to gdb itself, you can use the "+gdbparams" option whose argument is run through the pymake command line parser which uses Bourne shell splitting rules.  For example, if you wanted to run the command "show args" once gdb starts up, you would do:

$ ./mach debug +gdbparams "-ex 'show args'"

Alternatively, you can just run gdb directly against Firefox.  The downside is that you won't get magic stuff for free.  For example, mach sets an environment variable (see later on this page) to stop the JS engine from generating synthetic segfaults to support the slow script dialog mechanism.  It will likely also grow other useful capabilities.

$ gdb OBJDIR/dist/bin/firefox

See this old version for specialized instructions on older builds of Mozilla.

NOTE: Someone might need to update the rest of this page.

How do I pass arguments in prun?

Set the arguments in GDB before calling prun. Here's an example on how to do that:

(gdb) set args http://www.mozilla.org
(gdb) prun

How do I set a breakpoint in a library that hasn't been loaded?

Since GDB 6.1, GDB has support for "pending breakpoints".  This is controlled by the "set breakpoint pending" setting, and is enabled by default.  What this means is that a breakpoint that cannot be immediately resolved will be re-checked each time a shared library is loaded by the process being debugged.  If your GDB is older than this, you should upgrade.

In older versions, there isn't a way to set a breakpoint in GDB in a library that hasn't been loaded. If you are actually intersted in setting a breakpoint in a real component when it's loaded please see the section on setting a breakpoint when a component is loaded. If you are completely desperate and have to set a breakpoint in a library when it's loaded you can set a breakpoint on the symbol _dl_open. This function is called when a new library is loaded. You can set your breakpoint after you see that your library has been loaded.

How do I set a breakpoint when a component is loaded?

There's a faculity in XPCOM that allows you to set an environment variable that triggers the program to drop into the debugger when it loads a certain component. You have to set the XPCOM_BREAK_ON_LOAD environment variable before you run Mozilla. You set the variable to a string that contains the names of libraries you might want to load. For example, if you wanted to stop when a library named raptor or necko is loaded, you can set the variable to raptor:necko. Here's an example:

(gdb) set env XPCOM_BREAK_ON_LOAD raptor:necko
(gdb) prun

I can't set a breakpoint. Why not?

You probably can't set a breakpoint because the library in which that breakpoint is located hasn't been loaded. If you know the library has actually been loaded into Mozilla but you can't set the breakpoint see the section on loading shared libraries. If the library hasn't been loaded yet you will have to wait until it is. If you want to set a breakpoint as soon as the library is loaded, see the section on breaking when a component is loaded and breaking on a library load.

How do I display PRUnichar's?

Several people have suggested ways to do this:

(gdb) print ((PRUnichar*)uri.mBuffer)[0]@16
$47 = {114, 100, 102, 58, 110, 117, 108, 108, 0, 0, 8, 0, 0, 0, 37432,
16514}
 
(gdb) print aURI
$1 = (const PRUnichar *) 0x855e6e0
(gdb) x/32ch aURI
0x855e6e0:      104 'h' 116 't' 116 't' 112 'p' 58 ':'  47 '/'  47 '/'  119 'w'
0x855e6f0:      119 'w' 119 'w' 46 '.'  109 'm' 111 'o' 122 'z' 105 'i' 108 'l'
0x855e700:      108 'l' 97 'a'  46 '.'  111 'o' 114 'r' 103 'g' 47 '/'  115 's'
0x855e710:      116 't' 97 'a'  114 'r' 116 't' 47 '/'  0 '\0'  25 '\031'       0 '\0'
(gdb)
  • Define helper functions in your .gdbinit
# Define a "pu" command to display PRUnichar * strings (100 chars max)
# Also allows an optional argument for how many chars to print as long as
# it's less than 100.
def pu
  set $uni = $arg0
  if $argc == 2
    set $limit = $arg1
    if $limit > 100
      set $limit = 100
    end
  else
    set $limit = 100
  end
  # scratch array with space for 100 chars plus null terminator.  Make
  # sure to not use ' ' as the char so this copy/pastes well.
  set $scratch = "____________________________________________________________________________________________________"
  set $i = 0
  set $scratch_idx = 0
  while (*$uni && $i++ < $limit)
    if (*$uni < 0x80)
      set $scratch[$scratch_idx++] = *(char*)$uni++
    else
      if ($scratch_idx > 0)
	set $scratch[$scratch_idx] = '\0'
	print $scratch
	set $scratch_idx = 0
      end
      print /x *(short*)$uni++
    end
  end
  if ($scratch_idx > 0)
    set $scratch[$scratch_idx] = '\0'
    print $scratch
  end
end

# Define a "ps" command to display subclasses of nsAC?String.  Note that
# this assumes strings as of Gecko 1.9 (well, and probably a few
# releases before that as well); going back far enough will get you
# to string classes that this function doesn't work for.
def ps
  set $str = $arg0
  if (sizeof(*$str.mData) == 1 && ($str.mFlags & 1) != 0)
    print $str.mData
  else
    pu $str.mData $str.mLength
  end
end

This is hard. Give me a .gdbinit that already has the functions.

  • Define a small helper function "punichar" in #ifdef NS_DEBUG code somewhere.

How do I display an nsString?

You can call the ToNewCString() method on the nsString in question. It leaks a little memory but it shouldn't hurt anything if you only do it a few times in one gdb session. ( from akkana@netscape.com )

(gdb) p string.ToNewCString()

Another method (via bent) is the following (replace n with: the returned length of your string):

(gdb) p string.Length()
$1 = n
(gdb) x/ns string.BeginReading()

You can of course use any of the above unichar-printing routines instead of x/s.

This is hard. Give me a .gdbinit that works.

See Boris Zbarsky's .gdbinit. It contained several function definitions including:

  • "prun" to start the browser and disable library loading.
  • "pu" which will display a (PRUnichar *) string.
  • "ps" which will display a nsString.

How do I determine the concrete type of an object pointed to by an interface pointer?

You can determine the concrete type of any object pointed to by an XPCOM interface pointer by looking at the mangled name of the symbol for the object's vtable:

(gdb) p aKidFrame
$1 = (nsIFrame *) 0x85058d4
(gdb) x/wa *(void**)aKidFrame
0x4210d380 <__vt_14nsRootBoxFrame>: 0x0
(gdb) p *(nsRootBoxFrame*)aKidFrame
 [ all the member variables of aKidFrame ]

(If you're using gcc 3.x, the output is slightly different from the above (which is for gcc 2.9x), but just pay attention to the vtable symbol (which in this case would be _ZTV14nsRootBoxFrame) rather than the mangled name of the first virtual function, since some classes may not override the first virtual function (although they usually do, since it's QueryInterface).) Note that you won't get anything useful if the shared library containing the implementation of the object you're looking at is not loaded. See "How do I load shared libraries?" and "How do I see what libraries I already have loaded?".

Or, just use the gdb command "set print object on".

How can I debug JavaScript from gdb?

If you find JavaScript Engine code on the stack, you'll probably want to get a JS stack in addition to a C++ stack.

(gdb) call DumpJSStack() 

See http://www.mozilla.org/scriptable/javascript-stack-dumper.html for more JS debugging tricks.

How can I debug race conditions and/or how can I make something different happen at NS_ASSERTION time?

[submitted by Dan Mosedale]
Since Linux is unable to generate useful core files for multi-threaded applications, tracking down race-conditions that don't show up under the debugger can be a bit tricky. Unless you've given the --enable-crash-on-assert switch to configure, you can now change the behavior of NS_ASSERTION (actually nsDebug::Break) using the XPCOM_DEBUG_BREAK environment variable.  See the corresponding article for details on its possible values.

How do I run the debugger in emacs/xemacs?

Emacs and XEmacs contain modes for doing visual debugging that many programmers use. However, you might want to set up environment variables to make sure that when running the debugger and Mozilla they know where to load symbols from and where to find components. The easiest way to set up those environment variables is to use the run-mozilla.sh script that's in the dist/bin directory of your build. This script will set up the environment that you need to run the editor, shell or debugger. Another trick is to use the script to run /bin/bash ( or your favorite shell ) to set up a shell that has the right setup and then you can run any commands you want inside it.

[blizzard@gunhead bin]$ ./run-mozilla.sh /bin/bash
MOZILLA_FIVE_HOME=/home/blizzard/src/mozilla/build/dist/bin
  LD_LIBRARY_PATH=/home/blizzard/src/mozilla/build/dist/bin
     LIBRARY_PATH=/home/blizzard/src/mozilla/build/dist/bin
       SHLIB_PATH=/home/blizzard/src/mozilla/build/dist/bin
          LIBPATH=/home/blizzard/src/mozilla/build/dist/bin
       ADDON_PATH=/home/blizzard/src/mozilla/build/dist/bin
      MOZ_PROGRAM=/bin/bash
      MOZ_TOOLKIT=
        moz_debug=0
     moz_debugger=
[blizzard@gunhead bin]$

gdb 5 used to work for me, but now Mozilla won't start. What can I do?

A recent threading change (see bug 57051 for details) caused a problem on some systems: Mozilla would get partway through its initialization, then die before showing a window. A recent change to gdb has fixed this problem. Download and build [ http://sources.redhat.com/insight/ the latest version of Insight], or if you don't want a GUI, the latest version of gdb.

"run" or "prun" in GDB fails with "error in loading shared libraries."

Attempting to run mozilla-bin inside GDB fails with an error message similar to the following:

Starting program:
/u/dmose/s/mozilla/mozilla-all/mozilla/dist/bin/./mozilla-bin
/u/dmose/s/mozilla/mozilla-all/mozilla/dist/bin/./mozilla-bin: error
in loading shared libraries: libraptorgfx.so: cannot open shared
object file: No such file or directory

Your LD_LIBRARY_PATH is probably being reset by your .cshrc or .profile. From the GDB manual: *Warning:* GDB runs your program using the shell indicated by your 'SHELL' environment variable if it exists (or '/bin/sh' if not). If your 'SHELL' variable names a shell that runs an initialization file -- such as '.cshrc' for C-shell, or '.bashrc' for BASH--any variables you set in that file affect your program. You may wish to move setting of environment variables to files that are only run when you sign on, such as '.login' or '.profile'.

Debian's GDB doesn't work. What do I do?

[submitted by Bruce Mitchener]
Debian's unstable distribution currently uses glibc 2.1 and GDB 4.18. However, there is no package of GDB for Debian with the appropriate threads patches that will work with glibc 2.1. I was able to get this to work by getting the GDB 4.18 RPM from Red Hat's rawhide server and installing that. It has all of the patches necessary for debugging threaded software. These fixes are expected to be merged into GDB, which will fix the problem for Debian Linux.

Mozilla is aborting. Where do I set a breakpoint to find out where it is exiting?

On Linux there are two possible symbols that are causing this. They are PR_ASSERT() and NS_ASSERTION(). To catch the program before it exits to see where it's asserting you can stop at two places:

(gdb) b abort
(gdb) b exit

I keep getting a SIGSEGV in JS/JIT code under gdb even though there is no crash when gdb is not attached.  How do I fix it?

tl;dr: starting in FF28, set the JS_DISABLE_SLOW_SCRIPT_SIGNALS environment variable.

If JS runs to long, the browser needs to throw up the "Slow Script Dialog" to allow the user to stop runaway execution.  Normally, this requires testing some atomic variable, set by a watchdog thread, on every loop backedge, function prologue, etc.  This has a runtime cost.  To avoid this cost for hot JIT code, the JS engine uses a clever hack: when the script has potentially run for too long, the watchdog thread changes the memory protection bits of the JIT code so that execution immediately halts with a fault.  When the fault occurs, a JS-installed fault-handler function is run which recovers gracefully and allows the user to abort execution.  Unfortunately, gdb doesn't know intermediate fault is innocuous so it breaks in the same way as a real crash.  If you 'continue', then execution will proceed (into the handler, which recovers gracefully), but this is annoying and it can happen many times in a row.

The fix is to set a new environment variable (added in Firefox 28): JS_DISABLE_SLOW_SCRIPT_SIGNALS.  When this enviornment variable is set, the JS engine makes no attempt to interrupt Ion/Odin JIT code.  (JS code running in the baseline JIT or interpreter will be interrupted just fine.)  This means that an iloop in Ion/Odin will not be interruptible so this environment variable should only be used when debugging.

I keep getting a SIG32 in the debugger. How do I fix it?

If you are getting a SIG32 while trying to debug Mozilla you might have turned off shared library loading before the pthreads library was loaded, e.g. by having set auto-solib-add 0 in your .gdbinit file. In this case you can either:

  • Remove it and use the method explained in the section about GDB's memory usage instead.
  • Use handle SIG32 noprint either in gdb or in your .gdbinit file.

Alternatively the problem might lie in your pthread library. If this library has its symbols stripped, then GDB can't hook into thread events, and you end up with SIG32 signals. You can check if your libpthread is stripped by checking the output of file /lib/libpthread* for 'stripped'. To fix this problem for Gentoo Linux, you can re-emerge glibc after adding "nostrip" to your FEATURES in /etc/make.conf.

How do I get useful stack traces inside system libraries?

Many Linux distributions provide separate packages with debugging information for system libraries that you can install if you want gdb, valgrind, profiling tools, etc., to give useful stack traces for stacks that go through system libraries.

Fedora

On Fedora, you need to enable the debuginfo repositories, since the packages with debug symbols are provided in separate repositories. It is best to enable them permanently, since then you'll get the updates to the debug symbols when you get security updates for the packages. The best way I know to do this is simply to edit /etc/yum.repos.d/fedora.repo and fedora-updates.repo to change the enabled=0 line in the debuginfo section to enabled=1. Doing this will require dealing with the conflict (and redoing the change, I think) when upgrading to a new distribution version.

Once you do this, you can install debuginfo packages with yum or other package management tools. The best way to do this is to install the yum-utils package and then use the debuginfo-install command to install all the debuginfo:

# yum install yum-utils
# debuginfo-install firefox 

Installing a roughly complete set for debugging Mozilla can be done manually using:

 # yum install GConf2-debuginfo ORBit2-debuginfo atk-debuginfo \
 cairo-debuginfo dbus-debuginfo dbus-glib-debuginfo expat-debuginfo \
 fontconfig-debuginfo freetype-debuginfo gcc-debuginfo glib2-debuginfo \
 glibc-debuginfo gnome-vfs2-debuginfo gtk2-debuginfo gtk2-engines-debuginfo \
 hal-debuginfo libX11-debuginfo libXcursor-debuginfo libXext-debuginfo \
 libXfixes-debuginfo libXft-debuginfo libXi-debuginfo libXinerama-debuginfo \
 libXrender-debuginfo libbonobo-debuginfo libgnome-debuginfo \
 libselinux-debuginfo pango-debuginfo popt-debuginfo scim-bridge-debuginfo

Ubuntu 8.04

Ubuntu provides similar debug symbol packages for many of its libraries (although not all of them). To install them, simply run (to get a reasonably large set):

 $ sudo apt-get install libatk1.0-dbg libc6-dbg libcairo2-dbg \
 libfontconfig1-dbg libgcc1-dbg libglib2.0-0-dbg libgnomeui-0-dbg \
 libgnomevfs2-0-dbg libgnutls13-dbg libgtk2.0-0-dbg libice6-dbg \
 libjpeg62-dbg libpango1.0-0-dbg libpixman-1-0-dbg libstdc++6-4.2-dbg \
 libx11-6-dbg libx11-xcb1-dbg libxcb1-dbg libxft2-dbg zlib1g-dbg

 See also

Original Document Information

 

Document Tags and Contributors

Last updated by: asutherland@asutherland.org,