Debugging Firefox with GDB
==========================

This page details how you can more easily debug Firefox with gdb. :ref:`rr
<Debugging Firefox with 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. ``rr``
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 <https://www.gnu.org/software/ddd/>`__ or
`insight <https://sourceware.org/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.

.. code:: bash

   $ ./mach run --debugger=gdb

allows running Firefox in gdb, and debug the parent process. You can substitute
``run`` with another action, such as ``test``, ``mochitest``, ``xpcshell-test``,
etc.

Debugging child processes can be done by attaching the debugger.

.. code:: bash

   $ 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 ``about:processes``, using ``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
variable ``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.

.. code:: bash

   $ MOZ_DEBUG_CHILD_PROCESS=10 ./mach run
   ...
   ...
   ...

   CHILDCHILDCHILDCHILD (process type tab)
   debug me @ 65230

   ...
   ...
   ...

Attaching gdb to Firefox might fail on Linux distributions that enable common
kernel hardening features such as the Yama security module. If you encounter the
following error when attaching:

.. code:: bash

   $ gdb --pid <pid>
   ...
   ...

   Attaching to process <pid>
   ptrace: Operation not permitted.

Check the contents of `/proc/sys/kernel/yama/ptrace_scope`. If  it set to `1`
you won't be able to attach to processes, set it to `0` from a root shell:

.. code:: bash

  \# echo 0 > /proc/sys/kernel/yama/ptrace_scope

If you still can't attach check your setup carefully. Do not, under any
circumstances, run gdb as root. Since gdb can execute arbitrary code and spawn
shells it can be extremely dangerous to use it with root permissions.


Advanced gdb configuration
~~~~~~~~~~~~~~~~~~~~~~~~~~

The preferred method, is using the
:ref:`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
:ref:`mach is aware of mozconfigs <Configuring Build Options>`.

.. code:: bash

   $ ./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:

.. code:: bash

   $ ./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.

.. code::

   (gdb) $OBJDIR/dist/bin/firefox

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 :ref:`this section <Downloading
symbols on Linux / Mac OS X>` for setup instructions, it's just a matter of
sourcing a python script in ``.gdbinit``.

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:

.. code::

   (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?
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

.. code::

   (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:

.. code::

   (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``.

How can I debug JavaScript from gdb?
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

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

.. code::

   (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
<https://developer.mozilla.org/en-US/docs/Mozilla/Debugging/Debugging_JavaScript>`__
for more JS debugging tricks.

How can I debug race conditions
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Try :ref:`rr <Debugging Firefox with rr>` first. If this doesn't work, good
luck, maybe try :ref:`logging <Gecko Logging>` or sprinkling assertions.

I keep getting a SIGSYS, 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:

  .. code::

     add-auto-load-safe-path /path/to/mozilla-central

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:

  .. code::

    set debuginfod enabled on

in your ``.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.

When using ``debuginfod``, the correct information will be downloaded
automatically when needed (and subsequently cached).

If you're not sure what to use, there's a federated debuginfod server that
provides debug information for most mainstream distributions. You can use it
by adding the following line to your ``.gdbinit`` file:

  .. code::

    set debuginfod urls "https://debuginfod.elfutils.org/"

Keep in mind that it might take a while to download debug information the
very first time. This queries all the servers of multiple distributions
sequentially and debug information tends to be large. It will be cached for the
next run though.

Fedora
^^^^^^

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 ``/etc/yum.repos.d/fedora.repo`` and
``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:

.. code:: bash

   $ yum install yum-utils
   $ debuginfo-install firefox

This can be done manually using:

.. code:: bash

    $ yum install GConf2-debuginfo ORBit2-debuginfo atk-debuginfo \
    cairo-debuginfo dbus-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

Disabling multiprocess
~~~~~~~~~~~~~~~~~~~~~~

``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.

See also
~~~~~~~~~


-  `Mike Conley's blog post <https://mikeconley.ca/blog/2014/04/25/electrolysis-debugging-child-processes-of-content-for-make-benefit-glorious-browser-of-firefox>`__
-  `Performance tools <https://wiki.mozilla.org/Performance:Tools>`__
-  `Fun with
   gdb <https://blog.mozilla.com/sfink/2011/02/22/fun-with-gdb/>`__ by
   Steve Fink
