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