1.. -*- Mode: rst; fill-column: 80; -*-
2
3=====================
4Debugging Native Code
5=====================
6
7Table of contents
8=================
9
10.. contents:: :local:
11
12Debugging Native Code in Android Studio.
13========================================
14
15If you want to work on the C++ code that powers GeckoView, you will need
16to be able to perform native debugging inside Android Studio. This
17article will guide you through how to do that.
18
19If you need to get set up with GeckoView for the first time, follow the
20`Quick Start Guide <geckoview-quick-start.html>`_.
21
22Perform a debug build of Gecko.
23-------------------------------
24
251. Edit your ``mozconfig`` file and add the following lines. These will
26   ensure that the build includes debug checks and symbols.
27
28.. code::
29
30   ac_add_options --enable-debug
31   ac_add_options --with-android-ndk="<path>/.mozbuild/android-ndk-r17b"
32
332. Ensure that the following lines are commented out in your
34   ``mozconfig`` if present. ``./mach configure`` will not allow
35   artifact builds to be enabled when generating a debug build.
36
37.. code::
38
39   # ac_add_options --enable-artifact-builds
40
413. To be absolutely sure that Android Studio will pick up your debug
42   symbols, the first time you perform a debug build it is best to
43   clobber your ``MOZ_OBJDIR``. Subsequent builds should not need this
44   step.
45
46.. code:: bash
47
48   ./mach clobber
49
504. Build as usual. Because this is a debug build, and because you have
51   clobbered your ``MOZ_OBJDIR``, this will take a long time. Subsequent
52   builds will be incremental and take less time, so go make yourself a
53   cup of your favourite beverage.
54
55.. code:: bash
56
57   ./mach build
58
59Set up lldb to find your symbols
60--------------------------------
61
62Edit your ``~/.lldbinit`` file (or create one if one does not already
63exist) and add the following lines.
64
65The first line tells LLDB to enable inline breakpoints - Android Studio
66will need this if you want to use visual breakpoints.
67
68The remaining lines tell LLDB where to go to find the symbols for
69debugging.
70
71.. code:: bash
72
73   settings set target.inline-breakpoint-strategy always
74   settings append target.exec-search-paths <PATH>/objdir-android-opt/toolkit/library/build
75   settings append target.exec-search-paths <PATH>/objdir-android-opt/mozglue/build
76   settings append target.exec-search-paths <PATH>/objdir-android-opt/security
77
78Set up Android Studio to perform native debugging.
79==================================================
80
811. Edit the configuration that you want to debug by clicking
82   ``Run -> Edit Configurations...`` and selecting the correct
83   configuration from the options on the left hand side of the resulting
84   window.
852. Select the ``Debugger`` tab.
863. Select ``Dual`` from the ``Debug type`` select box. Dual will allow
87   debugging of both native and Java code in the same session. It is
88   possible to use ``Native``, but it will only allow for debugging
89   native code, and it’s frequently necessary to break in the Java code
90   that configures Gecko and child processes in order to attach
91   debuggers at the correct times.
924. Under ``Symbol Directories``, add a new path pointing to
93   ``<PATH>/objdir-android-opt/toolkit/library/build``, the same path
94   that you entered into your ``.lldbinit`` file.
955. Select ``Apply`` and ``OK`` to close the window.
96
97Debug Native code in Android Studio
98===================================
99
1001. The first time you are running a debug session for your app, it’s
101   best to start from a completely clean build. Click
102   ``Build -> Rebuild Project`` to clean and rebuild. You can also
103   choose to remove any existing builds from your emulator to be
104   completely sure, but this may not be necessary.
1052. If using Android Studio visual breakpoints, set your breakpoints in
106   your native code.
1073. Run the app in debug mode as usual.
1084. When debugging Fennec or geckoview_example, you will almost
109   immediately hit a breakpoint in ``ElfLoader.cpp``. This is expected.
110   If you are not using Android Studio visual breakpoints, you can set
111   your breakpoints here using the lldb console that is available now
112   this breakpoint has been hit. To set a breakpoint, select the app tab
113   (if running Dual, there will also be an ``<app> java`` tab) from the
114   debug window, and then select the ``lldb`` console tab. Type the
115   following into the console:
116
117.. code::
118
119   b <file>.cpp:<line number>
120
1215. Once your breakpoints have been set, click the continue execution
122   button to move beyond the ``ElfLoader`` breakpoint and your newly set
123   native breakpoints should be hit. Debug as usual.
124
125Attaching debuggers to content and other child processes
126--------------------------------------------------------
127
128Internally, GeckoView has a multi-process architecture. The main Gecko
129process lives in the main Android process, but content rendering and
130some other functions live in child processes. This balances load,
131ensures certain critical security properties, and allows GeckoView to
132recover if content processes become unresponsive or crash. However, it’s
133generally delicate to debug child processes because they come and go.
134
135The general approach is to make the Java code in the child process that
136you want to debug wait for a Java debugger at startup, and then to
137connect such a Java debugger manually from the Android Studio UI.
138
139`Bug 1522318 <https://bugzilla.mozilla.org/show_bug.cgi?id=1522318>`__
140added environment variables that makes GeckoView wait for Java debuggers
141to attach, making this debug process more developer-friendly. See
142`Configuring GeckoView for Automation <../consumer/automation.html>`__
143for instructions on how to set environment variables that configure
144GeckoView’s runtime environment.
145
146Making processes wait for a Java debugger
147~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
148
149The following environment variable makes the main (Gecko) process wait
150for a Java debugger to connect:
151
152.. code:: shell
153
154   MOZ_DEBUG_WAIT_FOR_JAVA_DEBUGGER=1
155
156This is a superset of Android Studio’s built-in debugging support so
157it’s not particularly useful (unless you want to attach a different jdwp
158debugger).
159
160The following environment variable makes every child process wait for a
161Java debugger to connect:
162
163.. code:: shell
164
165   MOZ_DEBUG_CHILD_WAIT_FOR_JAVA_DEBUGGER=
166
167Set ``MOZ_DEBUG_CHILD_WAIT_FOR_JAVA_DEBUGGER=suffix`` in the environment
168to make child processes with an Android process name ending with
169``suffix`` wait for a Java debugger to connect. For example, the
170following environment variable makes every child content process wait
171for a Java debugger to connect:
172
173.. code:: shell
174
175   MOZ_DEBUG_CHILD_WAIT_FOR_JAVA_DEBUGGER=:tab
176
177An easy way to set this is with ``./mach run``:
178
179.. code:: shell
180
181   ./mach run --setenv MOZ_DEBUG_CHILD_WAIT_FOR_JAVA_DEBUGGER=:tab
182
183Attaching a Java debugger to a waiting child process
184~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
185
186This is standard: follow the `Android Studio instructions <https://developer.android.com/studio/debug/index.html#attach-debugger>`_.
187You must attach a Java debugger, so you almost certainly want to attach
188a ``Dual`` debugger and you definitely can’t attach only a ``Native``
189debugger.
190
191Determining the correct process to attach to is a little tricky because
192the mapping from process ID (pid) to process name is not always clear.
193Gecko content child processes are suffixed ``:tab`` at this time.
194
195If you attach ``Dual`` debuggers to both the main process and a content
196child process, you will have four (4!) debug tabs to manage in Android
197Studio, which is awkward. Android Studio doesn’t appear to configure
198attached debuggers in the same way that it configures debuggers
199connecting to launched Run Configurations, so you may need to manually
200configure search paths – i.e., you may need to invoke the contents of
201your ``lldbinit`` file in the appropriate ``lldb`` console by hand,
202using an invocation like
203``command source /absolute/path/to/topobjdir/lldbinit``.
204
205Android Studio also doesn’t appear to support targeting breakpoints from
206the UI (say, from clicking in a gutter) to specific debug tabs, so you
207may also need to set breakpoints in the appropriate ``lldb`` console by
208hand.
209
210Managing more debug tabs may require different approaches.
211
212Using Android Studio on Windows
213~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
214
215You can now use :ref:`artifact builds <Understanding Artifact Builds>`
216mode on `MozillaBuild environment <https://wiki.mozilla.org/MozillaBuild>`_ even if you are
217not using WSL. If you want to debug GeckoView using Android Studio on
218Windows, you have to set an additional environment variable via the
219Control Panel to run the gradle script. The ``mach`` command sets these
220variables automatically, but Android Studio cannot.
221
222If you install MozillaBuild tools to ``C:\mozilla-build`` (default
223installation path), you have to set the ``MOZILLABUILD`` environment
224variable to recognize MozillaBuild installation path.
225
226To set environment variable on Windows 10, open the ``Control Panel``
227from ``Windows System``, then select ``System and Security`` -
228``System`` - ``Advanced system settings`` -
229``Environment Variables ...``.
230
231To set the ``MOZILLABUILD`` variable, click ``New...`` in
232``User variables for``, then ``Variable name:`` is ``MOZILLABUILD`` and
233``Variable value:`` is ``C:\mozilla-build``.
234
235You also have to append some tool paths to the ``Path`` environment
236variable.
237
238To append the variables to PATH, double click ``Path`` in
239``User Variables for``, then click ``New``. And append the following
240variables to ``Path``.
241
242-  ``%MOZILLABUILD%\msys\bin``
243-  ``%MOZILLABUILD%\bin``
244