355 lines
16 KiB
ReStructuredText
355 lines
16 KiB
ReStructuredText
Debugging On macOS
|
|
==================
|
|
|
|
This document explains how to debug Gecko-based applications such as
|
|
Firefox, Thunderbird, and SeaMonkey on macOS using Xcode. If you want to
|
|
debug from the terminal see :ref:`Debugging Mozilla with
|
|
lldb <Debugging Firefox with LLDB>`. For specific
|
|
information on a way to debug hangs, see :ref:`Debugging a hang on macOS <Debugging A Hang On macOS>`.
|
|
|
|
Creating a debuggable build
|
|
---------------------------
|
|
|
|
First, you need to build the application you're going to debug using
|
|
this in your .mozconfig
|
|
|
|
.. code::
|
|
|
|
ac_add_options --disable-optimize
|
|
ac_add_options --enable-debug-symbols
|
|
|
|
you can also add this flag if you want assertions etc. compiled in
|
|
|
|
.. code::
|
|
|
|
ac_add_options --enable-debug
|
|
|
|
See :ref:`Building Firefox for macOS <Building Firefox On MacOS>`
|
|
if you need help creating your own build.
|
|
|
|
Debugging Firefox on macOS 10.14+
|
|
---------------------------------
|
|
|
|
macOS 10.14 introduced Notarization and Hardened Runtime features for
|
|
improved application security. macOS 10.15 went further, requiring
|
|
applications to be Notarized with Hardened Runtime enabled in order to
|
|
launch (ignoring workarounds). When run on earlier macOS versions,
|
|
Notarization and Hardened Runtime settings have no effect.
|
|
|
|
Official Builds
|
|
~~~~~~~~~~~~~~~
|
|
|
|
At this time, official builds of Firefox 69 and later are Notarized.
|
|
**As a result, it is not possible to attach a debugger to these official
|
|
Firefox releases on macOS 10.14+ without disabling System Integrity
|
|
Protection (SIP).** This is due to Notarization requiring Hardened
|
|
Runtime to be enabled with the ``com.apple.security.get-task-allow``
|
|
entitlement disallowed. **Rather than disabling SIP (which has security
|
|
implications), it is recommended to debug with try builds or local
|
|
builds. The differences are explained below.**
|
|
|
|
try Server Builds
|
|
~~~~~~~~~~~~~~~~~
|
|
|
|
In most cases, developers needing to debug a build as close as possible
|
|
to the production environment should use a :ref:`try
|
|
build <Pushing to Try>`. These
|
|
builds enable Hardened Runtime and only differ from production builds in
|
|
that they are not Notarized which should not otherwise affect
|
|
functionality, (other than the ability to easily launch the browser on
|
|
macOS 10.15+ -- see quarantine note below). At this time, developers can
|
|
obtain a Hardened Runtime build with the
|
|
``com.apple.security.get-task-allow`` entitlement allowed by submitting
|
|
a try build and downloading the dmg generated by the "Rpk" shippable
|
|
build job. A debugger can be attached to Firefox processes of these
|
|
builds. try builds use the developer entitlement files from the
|
|
source tree (allowing debugger attach) while production builds use
|
|
the production versions (which must meet notarization requirements).
|
|
**On macOS 10.15+, downloaded try builds will not launch by default
|
|
because Notarization is required. To workaround this problem, remove the
|
|
quarantine extended attribute from the downloaded Nightly:**
|
|
|
|
``$ xattr -r -d com.apple.quarantine /Path/to/Nightly.app``
|
|
|
|
Local Builds
|
|
~~~~~~~~~~~~
|
|
|
|
Local builds of mozilla-central do not enable Hardened Runtime and hence
|
|
do not have debugging restrictions. As a result, some functionality will
|
|
be permitted on local builds, but blocked on production builds which
|
|
have Hardened Runtime enabled. `Bug
|
|
1522409 <https://bugzilla.mozilla.org/show_bug.cgi?id=1522409>`__ was
|
|
filed to automate codesigning local builds to enable Hardened Runtime by
|
|
default and eliminate this discrepancy.
|
|
|
|
Disabling System Integrity Protection (SIP)
|
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
|
|
If debugging a production build is required, follow Apple's documented
|
|
steps for disabling System Integrity Protection (SIP). Note that
|
|
disabling SIP bypasses Hardened Runtime restrictions which can mask some
|
|
bugs that only occur with Hardened Runtime so it is recommended to test
|
|
fixes with SIP enabled. **Disabling SIP has system security implications
|
|
that should be understood before taking this step.**
|
|
|
|
Creating an Xcode project
|
|
-------------------------
|
|
|
|
If you try to create a new Xcode project in an existing directory
|
|
then Xcode will delete its existing contents (Xcode will warn you
|
|
beforehand). To work around that, the steps below have you initialize
|
|
the project outside the Mozilla source tree, close the project, copy
|
|
the .xcodeproj project "file" into the source tree, and then reopen
|
|
the project to finish setting it up.
|
|
|
|
Note also that since Xcode 7.3.1 it doesn't seem to be possible to
|
|
have the Xcode project live outside the source tree. If you try to do
|
|
that then Xcode will simply **copy** the source files under the
|
|
project directory rather than link to them which breaks debugging and the
|
|
possibility to modify-rebuild-relaunch from inside Xcode.
|
|
|
|
These steps were last updated for Xcode 15:
|
|
|
|
#. Open Xcode, and create a new Project with File > New > Project.
|
|
Select the "Other" tab then select the "Empty" project type, then
|
|
click Next. Name the project and click Next again. Xcode will refuse
|
|
to create a new project in a non-empty directory, so create/select
|
|
a temporary directory to contain the project files, untick "Create
|
|
Git repository" and then click Create.
|
|
#. Before going any further, close the project (File > Close Project).
|
|
Now open Finder, find the \*.xcodejproj directory in the temporary
|
|
directory, move it into the root directory of your Mozilla source
|
|
tree, then double-click on it to reopen it. (You can now delete the
|
|
temporary directory.)
|
|
#. In the left-hand pane in Xcode you should see a tree item where the
|
|
root item has the project name. Now, right click on the root item,
|
|
select 'Add files to "<project-name>"', select all the files and
|
|
directories in your source directory, untick "Copy items if needed",
|
|
then click Add. (These will then be progressively added under the
|
|
root item <project-name> in the left-hand pane. Note that
|
|
subdirectories may initially appear to be empty, but they too will
|
|
progressively be populated as Xcode processes the sourse files.
|
|
Once done, you should be able to open any file quickly by hitting
|
|
Cmd-Shift-O and typing in the name of a file.)
|
|
#. In the Product menu, select Scheme > New Scheme and name your scheme
|
|
(for example, "Debug"). After you click OK, Xcode should open the
|
|
settings window for the new scheme. (If not, then open its settings
|
|
from the Product > Edit Scheme menu.)
|
|
#. Select "Run" on the left-hand side of the settings window, then
|
|
select the "Info" tab. Set the Executable by clicking on "None" and
|
|
selecting "Other...". A new dialog titled "Choose an executable to
|
|
launch" will pop up. Browse to the ``.app`` file that you want to
|
|
debug (``Firefox.app``, ``Nightly``\ ``Debug.app`` etc). The ``.app``
|
|
file is typically found inside the ``dist`` folder in your build
|
|
directory.
|
|
#. If you are debugging Firefox, Thunderbird, or some other application
|
|
that supports multiple profiles, using a separate profile for
|
|
debugging purposes is recommended. See "Having a profile for
|
|
debugging purposes" below. Select the "Arguments" tab in the scheme
|
|
editor, and click the '+' below the "Arguments passed on launch"
|
|
field. Add "-P *profilename*", where *profilename* is the name of a
|
|
profile you created previously.
|
|
#. Also in the "Arguments" panel, you may want to add an environment
|
|
variable MOZ_DEBUG_CHILD_PROCESS set to the value 1 to help with
|
|
debugging e10s.
|
|
#. Select "Build" from the left of the scheme editor window, and check
|
|
that there is nothing listed under Targets (otherwise it may cause
|
|
problems when you try to run the executable for debugging since you
|
|
will get build errors).
|
|
#. Click "Close" to close the scheme editor.
|
|
|
|
At this point you can run and debug the Mozilla application from Xcode
|
|
(if the application doesn't appear to open, check whether it opened in
|
|
the background). When it hits breakpoints, Xcode should open the
|
|
correct source file at the correct line.
|
|
|
|
Setting up lldb
|
|
---------------
|
|
|
|
``lldb`` is the debugger Xcode provides/uses.
|
|
|
|
.. warning::
|
|
|
|
One important issue that the Mozilla .lldbinit file fixes is that by
|
|
default some breakpoints will be listed as "pending", and Xcode will
|
|
not stop at them. If you don't include the Mozilla's .lldbinit, you
|
|
must at least put
|
|
``settings set target.inline-breakpoint-strategy always`` in your
|
|
``$HOME/.lldbinit`` as recommended on :ref:`Debugging Firefox with
|
|
lldb <Debugging Firefox with LLDB>`.
|
|
|
|
The
|
|
`.lldbinit <http://searchfox.org/mozilla-central/source/.lldbinit>`__
|
|
file in the source tree imports many useful `Mozilla specific lldb
|
|
settings, commands and
|
|
formatters <https://searchfox.org/mozilla-central/source/python/lldbutils/README.txt>`__
|
|
into ``lldb``, but you may need to take one of the following steps to
|
|
make sure this file is used.
|
|
|
|
If you are using ``lldb`` on the command line (independently of Xcode)
|
|
and you will always run it from either the top source directory, the
|
|
object directory or else the dist/bin subdirectory of the object
|
|
directory, then adding the following setting to your ``$HOME/.lldbinit``
|
|
is sufficient:
|
|
|
|
::
|
|
|
|
settings set target.load-cwd-lldbinit true
|
|
|
|
*However*, if you will run lldb from a different directory, or if you
|
|
will be running it indirectly by debugging in Xcode (Xcode always runs
|
|
lldb from "/"), then this setting will not help you. Instead, add the
|
|
following to your ``$HOME/.lldbinit``:
|
|
|
|
::
|
|
|
|
# This automatically sources the Mozilla project's .lldbinit as soon as lldb
|
|
# starts or attaches to a Mozilla app (that's in an object directory).
|
|
#
|
|
# This is mainly a workaround for Xcode not providing a way to specify that
|
|
# lldb should be run from a given directory. (Xcode always runs lldb from "/",
|
|
# regardless of what directory Xcode was started from, and regardless of the
|
|
# value of the "Custom working directory" field in the Scheme's Run options.
|
|
# Therefore setting `settings set target.load-cwd-lldbinit true` can't help us
|
|
# without Xcode providing that functionality.)
|
|
#
|
|
# The following works by setting a one-shot breakpoint to break on a function
|
|
# that we know will both run early (which we want when we start first start the
|
|
# app) and run frequently (which we want so that it will trigger ASAP if we
|
|
# attach to an already running app). The breakpoint runs some commands to
|
|
# figure out the object directory path from the attached target and then
|
|
# sources the .lldbinit from there.
|
|
#
|
|
# NOTE: This scripts actions take a few seconds to complete, so the custom
|
|
# formatters, commands etc. that are added may not be immediately available.
|
|
#
|
|
breakpoint set --name nsThread::ProcessNextEvent --thread-index 1 --auto-continue true --one-shot true
|
|
breakpoint command add -s python
|
|
# This script that we run does not work if we try to use the global 'lldb'
|
|
# object, since it is out of date at the time that the script runs (for
|
|
# example, `lldb.target.executable.fullpath` is empty). Therefore we must
|
|
# get the following objects from the 'frame' object.
|
|
target = frame.GetThread().GetProcess().GetTarget()
|
|
debugger = target.GetDebugger()
|
|
|
|
# Delete our breakpoint (not actually necessary with `--one-shot true`):
|
|
target.BreakpointDelete(bp_loc.GetBreakpoint().GetID())
|
|
|
|
# For completeness, find and delete the dummy breakpoint (the breakpoint
|
|
# lldb creates when it can't initially find the method to set the
|
|
# breakpoint on):
|
|
# BUG WORKAROUND! GetID() on the *dummy* breakpoint appears to be returning
|
|
# the breakpoint index instead of its ID. We have to add 1 to correct for
|
|
# that! :-(
|
|
dummy_bp_list = lldb.SBBreakpointList(target)
|
|
debugger.GetDummyTarget().FindBreakpointsByName("nsThread::ProcessNextEvent", dummy_bp_list)
|
|
dummy_bp_id = dummy_bp_list.GetBreakpointAtIndex(0).GetID() + 1
|
|
debugger.GetDummyTarget().BreakpointDelete(dummy_bp_id)
|
|
|
|
# "source" the Mozilla project .lldbinit:
|
|
os.chdir(target.executable.fullpath.split("/dist/")[0])
|
|
debugger.HandleCommand("command source -s true " + os.path.join(os.getcwd(), ".lldbinit"))
|
|
DONE
|
|
|
|
see :ref:`Debugging Mozilla with
|
|
lldb <Debugging Firefox with LLDB>`. for more information.
|
|
|
|
Having a profile for debugging purposes
|
|
---------------------------------------
|
|
|
|
It is recommended to create a separate profile to debug with, whatever
|
|
your task, so that you don't lose precious data like Bookmarks, saved
|
|
passwords, etc. So that you're not bothered with the profile manager
|
|
every time you start to debug, expand the "Executables" branch of the
|
|
"Groups & Files" list and double click on the Executable you added for
|
|
Mozilla. Click the plus icon under the "Arguments" list and type "-P
|
|
<profile name>" (e.g. "-P MozillaDebug"). Close the window when you're
|
|
done.
|
|
|
|
Running a debug session
|
|
-----------------------
|
|
|
|
Make sure breakpoints are active (which implies running under the
|
|
debugger) by opening the Product menu and selecting "Debug / Activate
|
|
Breakpoints" (also shown by the "Breakpoints" button in the top right
|
|
section of the main window). Then click the "Run" button or select "Run"
|
|
from the Product menu.
|
|
|
|
Setting breakpoints
|
|
~~~~~~~~~~~~~~~~~~~
|
|
|
|
Setting a breakpoint is easy. Just open the source file you want to
|
|
debug in Xcode, and click in the margin to the left of the line of code
|
|
where you want to break.
|
|
|
|
During the debugging session, each time that line is executed, the
|
|
debugger will break there, and you will be able to debug it.
|
|
|
|
.. warning::
|
|
|
|
Note that with the default configuration, some breakpoints will be
|
|
listed as "pending", and Xcode will not stop at them. If you don't
|
|
include the Mozilla's .lldbinit, you must at least put
|
|
``settings set target.inline-breakpoint-strategy always`` in your
|
|
``$HOME/.lldbinit`` as recommended on :ref:`Debugging Mozilla with
|
|
lldb <Debugging Firefox with LLDB>`.
|
|
|
|
Using Firefox-specific lldb commands
|
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
|
|
If you included the .lldbinit when `Setting up
|
|
lldb <#setting-up-lldb>`__, you can use Mozilla-specific lldb commands
|
|
in the console, located in the Debug area of Xcode. For example, type
|
|
``js`` to see the JavaScript stack. For more information, see :ref:`Debugging
|
|
Mozilla with lldb <Debugging Firefox with LLDB>`.
|
|
|
|
Debugging e10s child processes
|
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
|
|
Using Xcode to debug child processes created by an e10s-enabled browser
|
|
is a little trickier than debugging a single-process browser, but it can
|
|
be done. These directions were written using Xcode 6.3.1
|
|
|
|
#. Complete all the steps above under "Creating the Project"
|
|
#. From the "Product" menu, ensure the scheme you created is selected
|
|
under "Scheme", then choose "Scheme > Edit Scheme"
|
|
#. In the resulting popup, click "Duplicate Scheme"
|
|
#. Give the resulting scheme a more descriptive name than "Copy of
|
|
Scheme"
|
|
#. Select "Run" on the left-hand side of the settings window, then
|
|
select the "Info" tab. Set the Executable by clicking on the
|
|
"Executable" drop-down, and selecting the ``plugin-container.app``
|
|
that is inside the app bundle of the copy of Firefox you want to
|
|
debug.
|
|
#. On the same tab, under "Launch" select "Wait for executable to be
|
|
launched"
|
|
#. On the "Arguments" tab, remove all arguments passed on launch.
|
|
|
|
Now you're ready to start debugging:
|
|
|
|
#. From the "Product" menu, ensure the scheme you created above is
|
|
selected under "Scheme"
|
|
#. Click the "Run" button. The information area at the top of the window
|
|
will show "Waiting for plugin-container to launch"
|
|
#. From a command line, run your build of Firefox. When that launches a
|
|
child process (for example, when you start to load a webpage), Xcode
|
|
will notice and attach to that child process. You can then debug the
|
|
child process like you would any other process.
|
|
#. When you are done debugging, click the "Stop" button and quit the
|
|
instance of Firefox that you were debugging in the normal way.
|
|
|
|
For some help on using lldb see :ref:`Debugging Mozilla with
|
|
lldb <Debugging Firefox with LLDB>`.
|
|
|
|
Other resources
|
|
---------------
|
|
|
|
Apple has an extensive list of `debugging tips and
|
|
techniques <https://developer.apple.com/library/mac/#technotes/tn2124/_index.html>`__.
|
|
|
|
Questions? Problems?
|
|
~~~~~~~~~~~~~~~~~~~~
|
|
|
|
Try asking in our Element channels
|
|
`#developers <https://chat.mozilla.org/#/room/#developers:mozilla.org>`__ or
|
|
`#macdev <https://chat.mozilla.org/#/room/#macdev:mozilla.org>`__.
|