debugging_on_macos.rst (16516B)
1 Debugging On macOS 2 ================== 3 4 This document explains how to debug Gecko-based applications such as 5 Firefox, Thunderbird, and SeaMonkey on macOS using Xcode. If you want to 6 debug from the terminal see :ref:`Debugging Mozilla with 7 lldb <Debugging Firefox with LLDB>`. For specific 8 information on a way to debug hangs, see :ref:`Debugging a hang on macOS <Debugging A Hang On macOS>`. 9 10 Creating a debuggable build 11 --------------------------- 12 13 First, you need to build the application you're going to debug using 14 this in your .mozconfig 15 16 .. code:: 17 18 ac_add_options --disable-optimize 19 ac_add_options --enable-debug-symbols 20 21 you can also add this flag if you want assertions etc. compiled in 22 23 .. code:: 24 25 ac_add_options --enable-debug 26 27 See :ref:`Building Firefox for macOS <Building Firefox On MacOS>` 28 if you need help creating your own build. 29 30 Debugging Firefox on macOS 10.14+ 31 --------------------------------- 32 33 macOS 10.14 introduced Notarization and Hardened Runtime features for 34 improved application security. macOS 10.15 went further, requiring 35 applications to be Notarized with Hardened Runtime enabled in order to 36 launch (ignoring workarounds). When run on earlier macOS versions, 37 Notarization and Hardened Runtime settings have no effect. 38 39 Official Builds 40 ~~~~~~~~~~~~~~~ 41 42 At this time, official builds of Firefox 69 and later are Notarized. 43 **As a result, it is not possible to attach a debugger to these official 44 Firefox releases on macOS 10.14+ without disabling System Integrity 45 Protection (SIP).** This is due to Notarization requiring Hardened 46 Runtime to be enabled with the ``com.apple.security.get-task-allow`` 47 entitlement disallowed. **Rather than disabling SIP (which has security 48 implications), it is recommended to debug with try builds or local 49 builds. The differences are explained below.** 50 51 try Server Builds 52 ~~~~~~~~~~~~~~~~~ 53 54 In most cases, developers needing to debug a build as close as possible 55 to the production environment should use a :ref:`try 56 build <Pushing to Try>`. These 57 builds enable Hardened Runtime and only differ from production builds in 58 that they are not Notarized which should not otherwise affect 59 functionality, (other than the ability to easily launch the browser on 60 macOS 10.15+ -- see quarantine note below). At this time, developers can 61 obtain a Hardened Runtime build with the 62 ``com.apple.security.get-task-allow`` entitlement allowed by submitting 63 a try build and downloading the dmg generated by the "Rpk" shippable 64 build job. A debugger can be attached to Firefox processes of these 65 builds. try builds use the developer entitlement files from the 66 source tree (allowing debugger attach) while production builds use 67 the production versions (which must meet notarization requirements). 68 **On macOS 10.15+, downloaded try builds will not launch by default 69 because Notarization is required. To workaround this problem, remove the 70 quarantine extended attribute from the downloaded Nightly:** 71 72 ``$ xattr -r -d com.apple.quarantine /Path/to/Nightly.app`` 73 74 Local Builds 75 ~~~~~~~~~~~~ 76 77 Local builds of mozilla-central do not enable Hardened Runtime and hence 78 do not have debugging restrictions. As a result, some functionality will 79 be permitted on local builds, but blocked on production builds which 80 have Hardened Runtime enabled. `Bug 81 1522409 <https://bugzilla.mozilla.org/show_bug.cgi?id=1522409>`__ was 82 filed to automate codesigning local builds to enable Hardened Runtime by 83 default and eliminate this discrepancy. 84 85 Disabling System Integrity Protection (SIP) 86 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 87 88 If debugging a production build is required, follow Apple's documented 89 steps for disabling System Integrity Protection (SIP). Note that 90 disabling SIP bypasses Hardened Runtime restrictions which can mask some 91 bugs that only occur with Hardened Runtime so it is recommended to test 92 fixes with SIP enabled. **Disabling SIP has system security implications 93 that should be understood before taking this step.** 94 95 Creating an Xcode project 96 ------------------------- 97 98 If you try to create a new Xcode project in an existing directory 99 then Xcode will delete its existing contents (Xcode will warn you 100 beforehand). To work around that, the steps below have you initialize 101 the project outside the Mozilla source tree, close the project, copy 102 the .xcodeproj project "file" into the source tree, and then reopen 103 the project to finish setting it up. 104 105 Note also that since Xcode 7.3.1 it doesn't seem to be possible to 106 have the Xcode project live outside the source tree. If you try to do 107 that then Xcode will simply **copy** the source files under the 108 project directory rather than link to them which breaks debugging and the 109 possibility to modify-rebuild-relaunch from inside Xcode. 110 111 These steps were last updated for Xcode 15: 112 113 #. Open Xcode, and create a new Project with File > New > Project. 114 Select the "Other" tab then select the "Empty" project type, then 115 click Next. Name the project and click Next again. Xcode will refuse 116 to create a new project in a non-empty directory, so create/select 117 a temporary directory to contain the project files, untick "Create 118 Git repository" and then click Create. 119 #. Before going any further, close the project (File > Close Project). 120 Now open Finder, find the \*.xcodejproj directory in the temporary 121 directory, move it into the root directory of your Mozilla source 122 tree, then double-click on it to reopen it. (You can now delete the 123 temporary directory.) 124 #. In the left-hand pane in Xcode you should see a tree item where the 125 root item has the project name. Now, right click on the root item, 126 select 'Add files to "<project-name>"', select all the files and 127 directories in your source directory, untick "Copy items if needed", 128 then click Add. (These will then be progressively added under the 129 root item <project-name> in the left-hand pane. Note that 130 subdirectories may initially appear to be empty, but they too will 131 progressively be populated as Xcode processes the sourse files. 132 Once done, you should be able to open any file quickly by hitting 133 Cmd-Shift-O and typing in the name of a file.) 134 #. In the Product menu, select Scheme > New Scheme and name your scheme 135 (for example, "Debug"). After you click OK, Xcode should open the 136 settings window for the new scheme. (If not, then open its settings 137 from the Product > Edit Scheme menu.) 138 #. Select "Run" on the left-hand side of the settings window, then 139 select the "Info" tab. Set the Executable by clicking on "None" and 140 selecting "Other...". A new dialog titled "Choose an executable to 141 launch" will pop up. Browse to the ``.app`` file that you want to 142 debug (``Firefox.app``, ``Nightly``\ ``Debug.app`` etc). The ``.app`` 143 file is typically found inside the ``dist`` folder in your build 144 directory. 145 #. If you are debugging Firefox, Thunderbird, or some other application 146 that supports multiple profiles, using a separate profile for 147 debugging purposes is recommended. See "Having a profile for 148 debugging purposes" below. Select the "Arguments" tab in the scheme 149 editor, and click the '+' below the "Arguments passed on launch" 150 field. Add "-P *profilename*", where *profilename* is the name of a 151 profile you created previously. 152 #. Also in the "Arguments" panel, you may want to add an environment 153 variable MOZ_DEBUG_CHILD_PROCESS set to the value 1 to help with 154 debugging e10s. 155 #. Select "Build" from the left of the scheme editor window, and check 156 that there is nothing listed under Targets (otherwise it may cause 157 problems when you try to run the executable for debugging since you 158 will get build errors). 159 #. Click "Close" to close the scheme editor. 160 161 At this point you can run and debug the Mozilla application from Xcode 162 (if the application doesn't appear to open, check whether it opened in 163 the background). When it hits breakpoints, Xcode should open the 164 correct source file at the correct line. 165 166 Setting up lldb 167 --------------- 168 169 ``lldb`` is the debugger Xcode provides/uses. 170 171 .. warning:: 172 173 One important issue that the Mozilla .lldbinit file fixes is that by 174 default some breakpoints will be listed as "pending", and Xcode will 175 not stop at them. If you don't include the Mozilla's .lldbinit, you 176 must at least put 177 ``settings set target.inline-breakpoint-strategy always`` in your 178 ``$HOME/.lldbinit`` as recommended on :ref:`Debugging Firefox with 179 lldb <Debugging Firefox with LLDB>`. 180 181 The 182 `.lldbinit <http://searchfox.org/mozilla-central/source/.lldbinit>`__ 183 file in the source tree imports many useful `Mozilla specific lldb 184 settings, commands and 185 formatters <https://searchfox.org/mozilla-central/source/python/lldbutils/README.txt>`__ 186 into ``lldb``, but you may need to take one of the following steps to 187 make sure this file is used. 188 189 If you are using ``lldb`` on the command line (independently of Xcode) 190 and you will always run it from either the top source directory, the 191 object directory or else the dist/bin subdirectory of the object 192 directory, then adding the following setting to your ``$HOME/.lldbinit`` 193 is sufficient: 194 195 :: 196 197 settings set target.load-cwd-lldbinit true 198 199 *However*, if you will run lldb from a different directory, or if you 200 will be running it indirectly by debugging in Xcode (Xcode always runs 201 lldb from "/"), then this setting will not help you. Instead, add the 202 following to your ``$HOME/.lldbinit``: 203 204 :: 205 206 # This automatically sources the Mozilla project's .lldbinit as soon as lldb 207 # starts or attaches to a Mozilla app (that's in an object directory). 208 # 209 # This is mainly a workaround for Xcode not providing a way to specify that 210 # lldb should be run from a given directory. (Xcode always runs lldb from "/", 211 # regardless of what directory Xcode was started from, and regardless of the 212 # value of the "Custom working directory" field in the Scheme's Run options. 213 # Therefore setting `settings set target.load-cwd-lldbinit true` can't help us 214 # without Xcode providing that functionality.) 215 # 216 # The following works by setting a one-shot breakpoint to break on a function 217 # that we know will both run early (which we want when we start first start the 218 # app) and run frequently (which we want so that it will trigger ASAP if we 219 # attach to an already running app). The breakpoint runs some commands to 220 # figure out the object directory path from the attached target and then 221 # sources the .lldbinit from there. 222 # 223 # NOTE: This scripts actions take a few seconds to complete, so the custom 224 # formatters, commands etc. that are added may not be immediately available. 225 # 226 breakpoint set --name nsThread::ProcessNextEvent --thread-index 1 --auto-continue true --one-shot true 227 breakpoint command add -s python 228 # This script that we run does not work if we try to use the global 'lldb' 229 # object, since it is out of date at the time that the script runs (for 230 # example, `lldb.target.executable.fullpath` is empty). Therefore we must 231 # get the following objects from the 'frame' object. 232 target = frame.GetThread().GetProcess().GetTarget() 233 debugger = target.GetDebugger() 234 235 # Delete our breakpoint (not actually necessary with `--one-shot true`): 236 target.BreakpointDelete(bp_loc.GetBreakpoint().GetID()) 237 238 # For completeness, find and delete the dummy breakpoint (the breakpoint 239 # lldb creates when it can't initially find the method to set the 240 # breakpoint on): 241 # BUG WORKAROUND! GetID() on the *dummy* breakpoint appears to be returning 242 # the breakpoint index instead of its ID. We have to add 1 to correct for 243 # that! :-( 244 dummy_bp_list = lldb.SBBreakpointList(target) 245 debugger.GetDummyTarget().FindBreakpointsByName("nsThread::ProcessNextEvent", dummy_bp_list) 246 dummy_bp_id = dummy_bp_list.GetBreakpointAtIndex(0).GetID() + 1 247 debugger.GetDummyTarget().BreakpointDelete(dummy_bp_id) 248 249 # "source" the Mozilla project .lldbinit: 250 os.chdir(target.executable.fullpath.split("/dist/")[0]) 251 debugger.HandleCommand("command source -s true " + os.path.join(os.getcwd(), ".lldbinit")) 252 DONE 253 254 see :ref:`Debugging Mozilla with 255 lldb <Debugging Firefox with LLDB>`. for more information. 256 257 Having a profile for debugging purposes 258 --------------------------------------- 259 260 It is recommended to create a separate profile to debug with, whatever 261 your task, so that you don't lose precious data like Bookmarks, saved 262 passwords, etc. So that you're not bothered with the profile manager 263 every time you start to debug, expand the "Executables" branch of the 264 "Groups & Files" list and double click on the Executable you added for 265 Mozilla. Click the plus icon under the "Arguments" list and type "-P 266 <profile name>" (e.g. "-P MozillaDebug"). Close the window when you're 267 done. 268 269 Running a debug session 270 ----------------------- 271 272 Make sure breakpoints are active (which implies running under the 273 debugger) by opening the Product menu and selecting "Debug / Activate 274 Breakpoints" (also shown by the "Breakpoints" button in the top right 275 section of the main window). Then click the "Run" button or select "Run" 276 from the Product menu. 277 278 Setting breakpoints 279 ~~~~~~~~~~~~~~~~~~~ 280 281 Setting a breakpoint is easy. Just open the source file you want to 282 debug in Xcode, and click in the margin to the left of the line of code 283 where you want to break. 284 285 During the debugging session, each time that line is executed, the 286 debugger will break there, and you will be able to debug it. 287 288 .. warning:: 289 290 Note that with the default configuration, some breakpoints will be 291 listed as "pending", and Xcode will not stop at them. If you don't 292 include the Mozilla's .lldbinit, you must at least put 293 ``settings set target.inline-breakpoint-strategy always`` in your 294 ``$HOME/.lldbinit`` as recommended on :ref:`Debugging Mozilla with 295 lldb <Debugging Firefox with LLDB>`. 296 297 Using Firefox-specific lldb commands 298 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 299 300 If you included the .lldbinit when `Setting up 301 lldb <#setting-up-lldb>`__, you can use Mozilla-specific lldb commands 302 in the console, located in the Debug area of Xcode. For example, type 303 ``js`` to see the JavaScript stack. For more information, see :ref:`Debugging 304 Mozilla with lldb <Debugging Firefox with LLDB>`. 305 306 Debugging e10s child processes 307 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 308 309 Using Xcode to debug child processes created by an e10s-enabled browser 310 is a little trickier than debugging a single-process browser, but it can 311 be done. These directions were written using Xcode 6.3.1 312 313 #. Complete all the steps above under "Creating the Project" 314 #. From the "Product" menu, ensure the scheme you created is selected 315 under "Scheme", then choose "Scheme > Edit Scheme" 316 #. In the resulting popup, click "Duplicate Scheme" 317 #. Give the resulting scheme a more descriptive name than "Copy of 318 Scheme" 319 #. Select "Run" on the left-hand side of the settings window, then 320 select the "Info" tab. Set the Executable by clicking on the 321 "Executable" drop-down, and selecting the ``plugin-container.app`` 322 that is inside the app bundle of the copy of Firefox you want to 323 debug. 324 #. On the same tab, under "Launch" select "Wait for executable to be 325 launched" 326 #. On the "Arguments" tab, remove all arguments passed on launch. 327 328 Now you're ready to start debugging: 329 330 #. From the "Product" menu, ensure the scheme you created above is 331 selected under "Scheme" 332 #. Click the "Run" button. The information area at the top of the window 333 will show "Waiting for plugin-container to launch" 334 #. From a command line, run your build of Firefox. When that launches a 335 child process (for example, when you start to load a webpage), Xcode 336 will notice and attach to that child process. You can then debug the 337 child process like you would any other process. 338 #. When you are done debugging, click the "Stop" button and quit the 339 instance of Firefox that you were debugging in the normal way. 340 341 For some help on using lldb see :ref:`Debugging Mozilla with 342 lldb <Debugging Firefox with LLDB>`. 343 344 Other resources 345 --------------- 346 347 Apple has an extensive list of `debugging tips and 348 techniques <https://developer.apple.com/library/mac/#technotes/tn2124/_index.html>`__. 349 350 Questions? Problems? 351 ~~~~~~~~~~~~~~~~~~~~ 352 353 Try asking in our Element channels 354 `#developers <https://chat.mozilla.org/#/room/#developers:mozilla.org>`__ or 355 `#macdev <https://chat.mozilla.org/#/room/#macdev:mozilla.org>`__.