1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303
|
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
|