Debugging Firefox with GDB ========================== +--------------------------------------------------------------------+ | This page is an import from MDN and the contents might be outdated | +--------------------------------------------------------------------+ This page details how you can more easily debug Firefox and work around some GDB problems. Use GDB 5, or higher. A more recent version of GDB can be obtained from `sourceware `__ or your Linux distro repo. If you are running 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 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 do I run Firefox under gdb? ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 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 `. .. 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 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 How do I set a breakpoint in a library that hasn't been loaded? ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ GDB 6.1 and above has support for "pending breakpoints". This is controlled by the "``set breakpoint pending``" setting, and is enabled by default. If a breakpoint cannot be immediately resolved, it 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 breakpoints in a library that has not yet been loaded. See more on `setting a breakpoint when a component is loaded <#How_do_I_set_a_breakpoint_when_a_component_is_loaded.3F>`__. If you have to set a breakpoint you can set a breakpoint in ``_dl_open``. This function is called when a new library is loaded, when you can finally set your breakpoint. How do I set a breakpoint when a component is loaded? ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ In Firefox Version 57 (and possibly earlier) XPCOM_BREAK_ON_LOAD does not seem to exist. There's a facility in XPCOM which allows you to set an environment variable to drop into the debugger when loading a certain component. You have to set ``XPCOM_BREAK_ON_LOAD`` variable before you run Firefox, setting it to a string containing the names of libraries you want to load. For example, if you wish to stop when a library named ``raptor`` or ``necko`` is loaded, you set the variable to ``raptor:necko``. Here's an example: .. code:: (gdb) set env XPCOM_BREAK_ON_LOAD raptor:necko (gdb) prun Why can't I set a breakpoint? ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ You probably can't set a breakpoint because its library hasn't been loaded. Most Firefox functionality is in libraries loaded mid-way through the ``main()``\ function. If you break on ``main(),``\ and step through until the libraries are loaded, with a call to ``InitCOMGlue()``, you should be able to set breakpoints on many more symbols, source files, and continue running. .. code:: (gdb) break main (gdb) run Breakpoint 1, main(argc=4, argv=0x7fffffffde98, envp=0x7ffffffffdec0) ..... 256 { (gdb) next ... 293 nsresult rv = InitXPCOMGlue() (gdb) next If you still can't set the breakpoints, you need to confirm the library has loaded. You can't proceed until the library loads. See more on `loading shared libraries <#How_do_I_load_shared_libraries.3F>`__. If you wish to break as soon as the library is loaded, see the section on `breaking when a component is loaded <#How_do_I_set_a_breakpoint_when_a_component_is_loaded.3F>`__ and `breaking on a library load <#How_do_I_set_a_breakpoint_when_a_component_is_loaded.3F>`__. How do I display PRUnichar's? ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ One suggestion is this: .. code:: (gdb) print ((PRUnichar*)uri.mBuffer)[0]@16 $47 = {114, 100, 102, 58, 110, 117, 108, 108, 0, 0, 8, 0, 0, 0, 37432, 16514} .. code:: (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 .. code:: # 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. <#This_is_hard._Give_me_a_.gdbinit_that_works.>`__ - 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. It leaks a little memory but it shouldn't hurt anything if you only do it a few times in one gdb session. (via akkana@netscape.com) .. code:: (gdb) p string.ToNewCString() Another method (via bent) is the following (replace ``n`` with: the returned length of your string): .. code:: (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: .. 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 ] If you're using gcc 3.x, the output is slightly different from the gcc 2.9x output above. Pay particular attention to the vtable symbol, in this case ``__vt_14nsRootBoxFrame``. You won't get anything useful if the shared library containing the object is not loaded. See `How do I load shared libraries? <#How_do_I_load_shared_libraries.3F>`__ and `How do I see what libraries I already have loaded? <#How_do_I_see_what_libraries_I_already_have_loaded.3F>`__ 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() See `https://developer.mozilla.org/en-US/docs/Mozilla/Debugging/Debugging_JavaScript `__ 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] | As Linux is unable to generate useful core files for multi-threaded applications, tracking down race-conditions which 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`` (nsDebug::Break) using the ``XPCOM_DEBUG_BREAK`` environment variable. How do I run the debugger in emacs/xemacs? ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Emacs and XEmacs contain modes for doing visual debugging. However, you might want to set up environment variables, specifying the loading of symbols and components. The easiest way to set up these is to use the ``run-mozilla.sh`` script, located in the dist/bin directory of your build. This script sets up the environment to run the editor, shell, debugger, or defining a preferred setup and running any commands you wish. For example: .. code:: bash $ ./run-mozilla.sh /bin/bash MOZILLA_FIVE_HOME=/home/USER/src/mozilla/build/dist/bin LD_LIBRARY_PATH=/home/USER/src/mozilla/build/dist/bin LIBRARY_PATH=/home/USER/src/mozilla/build/dist/bin SHLIB_PATH=/home/USER/src/mozilla/build/dist/bin LIBPATH=/home/USER/src/mozilla/build/dist/bin ADDON_PATH=/home/USER/src/mozilla/build/dist/bin MOZ_PROGRAM=/bin/bash MOZ_TOOLKIT= moz_debug=0 moz_debugger= GDB 5 used to work for me, but now Firefox won't start. What can I do? ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ A recent threading change (see `bug 57051 `__ for details) caused a problem on some systems. Firefox would get part-way through its initialization, then stop before showing a window. A recent change to gdb has fixed this. Download and build `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." ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Running mozilla-bin inside GDB fails with an error message like: .. code:: 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 the 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? ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 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. (via `Bruce Mitchener `__) Firefox 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: ``PR_ASSERT()`` and ``NS_ASSERTION()``. To see where it's asserting you can stop at two places: .. code:: (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? ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Set the JS_DISABLE_SLOW_SCRIPT_SIGNALS environment variable (in FF33, the shorter and easier-to-remember JS_NO_SIGNALS). For an explanation, read `Jan's blog post `__. I keep getting a SIG32 in the debugger. How do I fix it? ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ If you are getting a SIG32 while trying to debug Firefox you might have turned off shared library loading before the pthreads library was loaded. For example, ``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 <#The_debugger_uses_a_lot_of_memory._How_do_I_fix_it.3F>`__ - 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 in ``file /lib/libpthread*`` and looking for ``'stripped'.``\ To fix this problem on 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, such as gdb, Valgrind, profiling tools, etc., to give useful stack traces via system libraries. 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 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 Debugging electrolysis (e10s) ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ``mach run`` and ``mach test`` both accept a ``--disable-e10s`` argument. Some debuggers can't catch child-process crashes without it. You can find some (outdated) information on https://wiki.mozilla.org/Electrolysis/Debugging. You may also like to read https://mikeconley.ca/blog/2014/04/25/electrolysis-debugging-child-processes-of-content-for-make-benefit-glorious-browser-of-firefox for a more up-to-date blog post. To get the child process id use: .. code:: MOZ_DEBUG_CHILD_PROCESS=1 mach run See also ~~~~~~~~~ - `Debugging `__ - `Performance tools `__ - `Fun with gdb `__ by Steve Fink - `Archer pretty printers for SpiderMonkey `__ (`blog post `__) - `More pretty printers `__ for Gecko internals (`blog post `__) .. container:: originaldocinfo .. rubric:: Original Document Information :name: Original_Document_Information - `History `__ - Copyright Information: © 1998-2008 by individual mozilla.org contributors; content available under a `Creative Commons license `__