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