Debugging Firefox with GDB¶
This page details how you can more easily debug Firefox with gdb. rr is most often a better choice to debug a problem,
but sometimes it isn’t possible to use it, such as attempting to reproduce a
race condition or when performance is important to reproduce an issue.
chaos mode allows reproducing a lot of issues though, and should be tried.
Where can I find general gdb documentation?¶
Using GDB is beyond the scope of this document. Documentation is likely available on your system if you have GDB installed, in the form of info, man pages, or the gnome help browser. Additionally, you can use a graphical front-end to GDB like ddd or insight. For more information see https://sourceware.org/gdb/current/onlinedocs/gdb/
How to debug Firefox with gdb?¶
Firefox is a multiprocess application, with a parent process, and several child processes, each specialized and sandboxed differently.
$ ./mach run --debugger=gdb
allows running Firefox in gdb, and debug the parent process. You can substitute
run with another action, such as
Debugging child processes can be done by attaching the debugger.
$ gdb --pid <pid>
There’s a number of ways to find the PID of a process: hovering over a tab for a
content process, opening
ps ... | grep firefox on
the command line, etc.
Sometimes, it is desirable to attach to a child process at startup, to diagnose
something very close to the start of the process. Setting the environment
MOZ_DEBUG_CHILD_PROCESS=10 will make each new process print an few
informative lines, including the process type and its PID. The process with then
sleep for a number of seconds equal to the value of the environment variable,
allowing to attach a debugger.
$ MOZ_DEBUG_CHILD_PROCESS=10 ./mach run ... ... ... CHILDCHILDCHILDCHILD (process type tab) debug me @ 65230 ... ... ...
Advanced gdb configuration¶
The preferred method, is using the Mach command-line tool to run the debugger, which can bypass several optional defaults. Use “mach help run” to get more details. If inside the source directory, you would use “./mach”. Please note that mach is aware of mozconfigs.
$ ./mach run --debug [arguments to pass to firefox]
If you need to direct arguments to gdb, you can use ‘–debugger-args’ options via the command line parser, taking care to adhere to shell splitting rules. For example, if you wanted to run the command ‘show args’ when gdb starts, you would use:
$ ./mach run --debug --debugger-args "-ex 'show args'"
Alternatively, you can run gdb directly against Firefox. However, you won’t get some of the more useful capabilities this way. For example, mach sets an environment variable (see below) to stop the JS engine from generating synthetic segfaults to support the slower script dialoging mechanism.
How to debug a Firefox in the field (not compiled on the host)¶
If you need to attach to a Firefox process live on a machine, and this Firefox
was built by Mozilla, or by certain Linux distros, it’s possible to get symbols
and sources using the Mozilla symbol server, see this section for setup instructions, it’s just a matter of
sourcing a python script in
Debugging then works as usual, except the build probably has a very high optimization level.
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 https://www.mozilla.org (gdb) prun
Why breakpoints seem to not be hit?¶
The most likely cause is that gdb hasn’t been attached to the process in which the code to diagnose is ran. Enabling the relevant MOZ_LOG modules can help, since by default it prints the process type and pid of all logging statements.
break list will display a list of breakpoints, and whether or not they’re enabled. C++ namespaces need to be specified entirely, and it’s sometimes hard to break in lambda. Breaking by line number is an alternative strategy that often works in this case.
How do I display an nsString?¶
(gdb) p ToNewCString(string);
This leaks a bit of memory but it doesn’t really matter.
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 ]
Or use the gdb command
set print object on.
(gdb) call DumpJSStack()
Please note that if gdb has been attached to a process, the stack might be printed in the terminal window in which Firefox was started.
See this MDN page for more JS debugging tricks.
How can I debug race conditions¶
I keep getting a SIG32, or SIGSEGV in JS/JIT code under gdb even though there is no crash when gdb is not attached. How do I fix it?¶
Allow gdb to read mozilla-central’s .gdbinit, located at build/.gdbinit. In your own .gdbinit, add the line:
How do I get useful stack traces inside system libraries?¶
Many Linux distributions provide separate packages with debugging information for system libraries, such as gdb, Valgrind, profiling tools, etc., to give useful stack traces via system libraries.
The modern way to do this is to enable
debuginfod. This can be done by adding:
set debuginfod enabled on
.gdbinit, but there might be distro-specific instructions.
Alternatively, you can install the packages that contain the debug symbols for
the libraries you want to debug.
debuginfod, the correct information will be downloaded
automatically when needed (and subsequently cached).
On Fedora, you need to enable the debuginfo repositories, as the
packages are in separate repositories. Enable them permanently, so when
you get updates you also get security updates for these packages. A way
to do this is edit
fedora-updates.repo to change the
enabled=0 line in the
debuginfo section to
enabled=1. This may then flag a conflict when
upgrading to a new distribution version. You would the need to perform
this edit again.
You can finally install debuginfo packages with yum or other package
management tools. The best way is install the
yum-utils package, and
then use the
debuginfo-install command to install all the debuginfo:
$ yum install yum-utils $ debuginfo-install firefox
This 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
mach run and
mach test both accept a
--disable-e10s argument. Some
debuggers can’t catch child-process crashes without it. This is sometimes a
viable alternative to attaching, but these days it changes enough thing that
it’s not always a usable option.