blob: 420faef475523a7d4e1d82e671c3ecf2e9ccfb0c [file] [log] [blame] [view]
Robert Sesek92b004c2021-04-08 20:12:341# Debugging Chromium on macOS
2
3[TOC]
4
Robert Sesek3b731b12021-04-14 21:54:035## Debug vs. Release Builds
Robert Sesek92b004c2021-04-08 20:12:346
Robert Sesek3b731b12021-04-14 21:54:037Debug builds are the default configuration for Chromium and can be explicitly
8specified with `is_debug=true` in the `args.gn` file of the out directory. Debug
9builds are larger and non-portable because they default to
10`is_component_build=true`, but they contain full debug information.
Robert Sesek92b004c2021-04-08 20:12:3411
Robert Sesek3b731b12021-04-14 21:54:0312If you set `is_debug=false`, a release build will be created with no symbol
13information, which cannot be used for effective debugging.
Robert Sesek92b004c2021-04-08 20:12:3414
Robert Sesek3b731b12021-04-14 21:54:0315A middle-ground is to set `symbol_level=1`, which will produce a minimal symbol
16table, capable of creating backtraces, but without frame-level local variables.
17This is faster to build than a debug build, but it is less useful for debugging.
Robert Sesek92b004c2021-04-08 20:12:3418
Robert Sesek3b731b12021-04-14 21:54:0319When doing an `is_official_build=true` build (which is meant for producing
20builds with full compiler optimization suitable for shipping to users),
21`enable_dsyms` and `enable_stripping` both get set to `true`. The binary itself
22will be stripped of its symbols, but the debug information will be saved off
23into a dSYM file. Producing a dSYM is rather slow, so it is uncommon for
24developers to build with this configuration.
Robert Sesek92b004c2021-04-08 20:12:3425
Robert Sesek3b731b12021-04-14 21:54:0326### Chrome Builds
Robert Sesek92b004c2021-04-08 20:12:3427
Robert Sesek3b731b12021-04-14 21:54:0328The official Google Chrome build has published dSYMs that can be downloaded with
29the script at `tools/mac/download_symbols.py` or by using the LLDB integration
30at `tools/lldb/lldb_chrome_symbols.py`.
Robert Sesek92b004c2021-04-08 20:12:3431
Robert Sesek3b731b12021-04-14 21:54:0332However, the official Chrome build is
33[codesigned](../../chrome/installer/mac/signing/README.md) with the `restrict`
34and `runtime` options, which generally prohibit debuggers from attaching.
Robert Sesek92b004c2021-04-08 20:12:3435
Robert Sesek3b731b12021-04-14 21:54:0336In order to debug production/released Chrome, you need to do one of two things:
Robert Sesek92b004c2021-04-08 20:12:3437
Robert Sesek3b731b12021-04-14 21:54:03381. Disable [System Integrity
39Protection](https://developer.apple.com/documentation/security/disabling_and_enabling_system_integrity_protection),
40by:
41 1. Rebooting into macOS recovery mode
42 2. Launching Terminal
43 3. Running `csrutil enable --without debug`
44 4. Rebooting
452. Stripping or force-re-codesigning the binary to not use those options:
46 `codesign --force --sign - path/to/Google\ Chrome.app`
Robert Sesek92b004c2021-04-08 20:12:3447
Robert Sesek3b731b12021-04-14 21:54:0348If you will frequently debug official builds, (1) is recommended. Note that
49disabling SIP reduces the overall security of the system, so your system
50administrator may frown upon it.
Robert Sesek92b004c2021-04-08 20:12:3451
Robert Sesek3b731b12021-04-14 21:54:0352## The Debugger
Robert Sesek92b004c2021-04-08 20:12:3453
Robert Sesek3b731b12021-04-14 21:54:0354The debugger on macOS is `lldb` and it is included in both a full Xcode install
55and the Command Line Tools package. There are two ways to use LLDB: either
56launching Chromium directly in LLDB, or attaching to an existing process:
Robert Sesek92b004c2021-04-08 20:12:3457
Robert Sesek3b731b12021-04-14 21:54:0358 lldb ./out/debug/Chromium.app/Contents/MacOS/Chromium
59 lldb -p <pid>
Robert Sesek92b004c2021-04-08 20:12:3460
Robert Sesek3b731b12021-04-14 21:54:0361LLDB has an extensive help system which you can access by typing `help` at the
62`(lldb)` command prompt. The commands are organized into a functional hierarchy,
63and you can explore the subcommands via `(lldb) help breakpoint`, etc. Commands
64can take arguments in a command-line flag style. Many commands also have short
65mnemonics that match the `gdb` equivalents. You can also just use enough letters
66to form a unique prefix of the command hierarchy. E.g., these are equivalent:
Robert Sesek92b004c2021-04-08 20:12:3467
Robert Sesek3b731b12021-04-14 21:54:0368 (lldb) help breakpoint set
69 (lldb) h br s
Robert Sesek92b004c2021-04-08 20:12:3470
Robert Sesek3b731b12021-04-14 21:54:0371When the program is running, you can use **Ctrl-C** to interrupt it and pause
72the debugger.
Robert Sesek92b004c2021-04-08 20:12:3473
Robert Sesek3b731b12021-04-14 21:54:0374### Passing Arguments
Robert Sesek92b004c2021-04-08 20:12:3475
Robert Sesek3b731b12021-04-14 21:54:0376To pass arguments to LLDB when starting Chromium, use a `--`:
Robert Sesek92b004c2021-04-08 20:12:3477
Robert Sesek3b731b12021-04-14 21:54:0378 lldb ./out/debug/Chromium.app/contents/MacOS/Chromium -- --renderer-startup-dialog
Robert Sesek92b004c2021-04-08 20:12:3479
Robert Sesek3b731b12021-04-14 21:54:0380### Breakpoints
Robert Sesek92b004c2021-04-08 20:12:3481
Robert Sesek3b731b12021-04-14 21:54:0382Simple function-name breakpoints can be specified with a short mnemonic:
Robert Sesek92b004c2021-04-08 20:12:3483
Robert Sesek3b731b12021-04-14 21:54:0384 (lldb) b BrowserWindow::Close
Robert Sesek92b004c2021-04-08 20:12:3485
Robert Sesek3b731b12021-04-14 21:54:0386But there are a range of other options for setting breakpoints using the, such
87as:
88* `-t` to limit the breakpoint to a specific thread
89* `-s` to specify a specific shared library, if the same symbol name is exported
90 by multiple libraries
91* `-o` for a one-shot breakpoint (delete after first hit)
Robert Sesek92b004c2021-04-08 20:12:3492
Robert Sesek3b731b12021-04-14 21:54:0393See `(lldb) help br set` for full details.
Robert Sesek92b004c2021-04-08 20:12:3494
Robert Sesek3b731b12021-04-14 21:54:0395### Navigating the Stack
Robert Sesek92b004c2021-04-08 20:12:3496
Robert Sesek3b731b12021-04-14 21:54:0397When the debugger is paused, you can get a backtrace by typing `bt`. To navigate
98the stack by-1 type either `up` or `down`. You can also jump to a specific index
99in the stack by typing `f #` (short for `frame select #`).
100
101To see local variables, type `v` (short for `frame variable`).
102
103### Examining Execution
104
105To step through the program, use:
106
107* `s` or `si` for step-in
108* `n` for step-over
109* `c` to continue (resume or go to next breakpoint)
110
111### Printing Values
112
113To print values, use the `p <value>` command, where `<value>` can be a variable,
114a variable expression like `object->member_->sub_member_.value`, or an address.
115
116If `<value>` is a pointer to a structure, `p <value>` will usually just print
117the address. To show the contents of the structure, dereference the value. E.g.:
118
119 (lldb) p item
120 (HistoryMenuBridge::HistoryItem *) $3 = 0x0000000245ef5b30
121 (lldb) p *item
122 (HistoryMenuBridge::HistoryItem) $4 = {
123 title = u"Google"
124 url = {
125 spec_ = "https://www.google.com/"
126 is_valid_ = true
127
128
129Note above that LLDB has also stored the results of the expressions passed to
130`p` into the variables `$3` and `$4`, which can be referenced in other LLDB
131expressions.
132
133Often (and always when printing addresses) there is not type information to
134enable printing the full structure of the referenced memory. In these cases, use
135a C-style cast:
136
137 (lldb) p 0x0000000245ef5b30 # Does not have type information
138 (long) $5 = 9763248944
139 (lldb) p (HistoryMenuBridge::HistoryItem*)0x0000000245ef5b30
140 (HistoryMenuBridge::HistoryItem *) $6 = 0x0000000245ef5b30
141 (lldb) p *$6
142 (HistoryMenuBridge::HistoryItem) $7 = {
143 title = u"Google"
144
145
146* For printing Cocoa NSObjects, use the `po` command to get the `-[NSObject description]`.
147* If `uptr` is a `std::unique_ptr`, the address it wraps is accessible as
148 `uptr.__ptr_.__value_`.
149* To pretty-print `std::u16string`, follow the instructions [here](../lldbinit.md).
150
151## Multi-Process Debugging
152
153Chrome is split into multiple processes, which can mean that the logic you want
154to debug is in a different process than the main browser/GUI process. There are
155a few ways to debug the multi-process architecture, discussed below.
156
157### (a) Attach to a Running Process
158
159You can use Chrome's Task Manager to associate specific sites with their PID.
160Then simply attach with LLDB:
161
162 lldb -p <pid>
163
164Or, if you have already been debugging a Chrome process and want to retain your
165breakpoints:
166
167 (lldb) attach <pid>
168
169### (b) Debug Process Startup
170
171If you need to attach early in the child process's lifetime, you can use one of
172these startup-dialog switches for the relevant process type:
173
174* `--renderer-startup-dialog`
175* `--utility-startup-dialog`
Alex Gough20926742021-05-13 20:11:30176* `--utility-startup-dialog=data_decoder.mojom.DataDecoderService`
Robert Sesek3b731b12021-04-14 21:54:03177
178After the process launches, it will print a message like this to standard error:
179
180 [80156:775:0414/130021.862239:ERROR:content_switches_internal.cc(112)] Renderer (80156) paused waiting for debugger to attach. Send SIGUSR1 to unpause.
181
182Then attach the the process like above in **(a)**, using the PID in parenthesis
183(e.g. *80156* above).
184
185### (c) Run Chrome in a single process
186
187> This option is not recommended. Single-process mode is not tested and is
188> frequently broken.
189
190Chrome has an option to run all child processes as threads inside a single
191process, using the `--single-process` command line flag. This can make debugging
192easier.
193
194## Debugging Out-of-Process Tests:
195
196Similar to debugging the renderer process, simply attaching LLDB to a
Robert Sesek92b004c2021-04-08 20:12:34197out-of-process test like browser\_tests will not hit the test code. In order to
198debug a browser test, you need to run the test binary with  "`--single_process`"
199(note the underscore in `single_process`). Because you can only run one browser
200test in the same process, you're probably going to need to add `--gtest_filter`
201as well. So your command will look like this:
202
Robert Sesek3b731b12021-04-14 21:54:03203 /path/to/src/out/debug/browser_tests --single_process --gtest_filter=GoatTeleporterTest.DontTeleportSheep
Robert Sesek92b004c2021-04-08 20:12:34204
Robert Sesek3b731b12021-04-14 21:54:03205## Working with Xcode
Robert Sesek92b004c2021-04-08 20:12:34206
Robert Sesek3b731b12021-04-14 21:54:03207If you'd prefer to use Xcode GUI to use the debugger, there are two options:
Robert Sesek92b004c2021-04-08 20:12:34208
Robert Sesek3b731b12021-04-14 21:54:03209### (1) Empty Xcode Project
Robert Sesek92b004c2021-04-08 20:12:34210
Robert Sesek3b731b12021-04-14 21:54:03211This approach creates an empty Xcode project that only provides a GUI debugger:
Robert Sesek92b004c2021-04-08 20:12:34212
Robert Sesek3b731b12021-04-14 21:54:032131. Select **File** > **New** > **Project...** and make a new project. Dump it
214 anywhere, call it anything. It doesn't matter.
2152. Launch Chromium.
2163. In Xcode, select **Debug** > **Attach to Process** > *Chromium*.
2174. You can now pause the process and set breakpoints. The debugger will also
218 activate if a crash occurs.
Robert Sesek92b004c2021-04-08 20:12:34219
Robert Sesek3b731b12021-04-14 21:54:03220### (2) Use *gn*
Robert Sesek92b004c2021-04-08 20:12:34221
Robert Sesek3b731b12021-04-14 21:54:032221. Tell `gn` to generate an Xcode project for your out directory:
223 `gn gen --ide=xcode out/debug`
2242. Open *out/debug/all.xcodeproj*
2253. Have it automatically generate schemes for you
2264. You can now build targets from within Xcode, which will simply call out to
227 `ninja` via an Xcode script. But the resulting binaries are available as
228 debuggable targets in Xcode.
Robert Sesek92b004c2021-04-08 20:12:34229
Robert Sesek3b731b12021-04-14 21:54:03230Note that any changes to the .xcodeproj will be overwritten; all changes to the
231build system need to be done in GN.
Robert Sesek92b004c2021-04-08 20:12:34232
Robert Sesek3b731b12021-04-14 21:54:03233## Debugging the Sandbox
Robert Sesek92b004c2021-04-08 20:12:34234
Robert Sesek3b731b12021-04-14 21:54:03235See the page on [sandbox debugging](sandbox_debugging.md).
Robert Sesek92b004c2021-04-08 20:12:34236
Robert Sesek3b731b12021-04-14 21:54:03237## System Permission Prompts; Transparency, Consent, and Control (TCC)
Robert Sesek92b004c2021-04-08 20:12:34238
Robert Sesek3b731b12021-04-14 21:54:03239When debugging issues with OS-mediated permissions (e.g. Location, Camera,
240etc.), you need to launch Chromium with LaunchServices rather than through a
241shell. If you launch Chromium as a subprocess of your terminal shell, the
242permission requests get attributed to the terminal app rather than Chromium.
Robert Sesek92b004c2021-04-08 20:12:34243
Robert Sesek3b731b12021-04-14 21:54:03244To launch Chromium via launch services, use the `open(1)` command:
Robert Sesek92b004c2021-04-08 20:12:34245
Robert Sesek3b731b12021-04-14 21:54:03246 open ./out/debug/Chromium.app
Robert Sesek92b004c2021-04-08 20:12:34247
Robert Sesek3b731b12021-04-14 21:54:03248To pass command line arguments:
Robert Sesek92b004c2021-04-08 20:12:34249
Robert Sesek3b731b12021-04-14 21:54:03250 open ./out/debug/Chromium.app -- --enable-features=MyCoolFeature
Robert Sesek92b004c2021-04-08 20:12:34251
252## Taking CPU Samples
253
254A quick and easy way to investigate slow or hung processes is to use the sample
255facility, which will generate a CPU sample trace. This can be done either in the
256Terminal with the sample(1) command or by using Activity Monitor:
257
Robert Sesek3b731b12021-04-14 21:54:032581. Open Activity Monitor
2592. Find the process you want to sample (for "Helper" processes, you may want to
260 consult the Chrome Task Manager)
2613. Double-click on the row
2624. Click the **Sample** button in the process's information window
Robert Sesek92b004c2021-04-08 20:12:34263
264After a few seconds, the sample will be completed. For official Google Chrome
265builds, the sample should be symbolized using
266[crsym](https://goto.google.com/crsym/). If you do not have access to crsym,
267save the *entirecontents as a file and attach it to a bug report for later
268analysis.
269
270See also [How to Obtain a Heap
271Dump](../memory-infra/heap_profiler.md#how-to-obtain-a-heap-dump-m66_linux_macos_windows).
Robert Sesek3b731b12021-04-14 21:54:03272
273## Working with Minidumps
274
275[See this
276page.](https://sites.google.com/a/chromium.org/dev/developers/crash-reports)
277
278## Disabling ReportCrash
279
280macOS helpfully tries to write a crash report every time a binary crashes
281which happens for example when a test in unit\_tests fails. Since Chromium's
282debug binaries are huge, this takes forever. If this happens, "ReportCrash" will
283be the top cpu consuming process in Activity Monitor. You should disable
284ReportCrash while you work on Chromium. Run `man ReportCrash` to learn how to do
285this on your version of macOS. On 10.15, the command is
286
287 launchctl unload -w /System/Library/LaunchAgents/com.apple.ReportCrash.plist
288 sudo launchctl unload -w /System/Library/LaunchDaemons/com.apple.ReportCrash.Root.plist
289
290Yes, you need to run this for both the normal user and the admin user.
291
292## Processing Apple Crash Reports
293
294If you get a Google Chrome crash report caught by ReportCrash/macOS, it will not
295have symbols (every frame will be ChromeMain). To get a symbolized stack trace,
296use the internal [crsym](httsp://goto.google.com/crsym) tool by simply pasting
297the contents of an entire Apple crash report.
298
299## Testing Other Locales
300
301To test Chrome in a different locale, change your system locale via the System
302Preferences. (Keep the preferences window open so that you can change the
303locale back without needing to navigate through menus in a language you may not
304know.)
305
306## Using DTrace
307
308DTrace is a powerful, kernel-level profiling and dynamic tracing utility. In
309order to use DTrace, you need to (at least partially) disable System Integrity
310Protection with ([see above](#Chrome-Builds)):
311
312 csrutil enable --without dtrace
313
314Using DTrace is beyond the scope of this document, but the following resources
315are useful:
316
317* [jgm's awesome introductory article](http://www.mactech.com/articles/mactech/Vol.23/23.11/ExploringLeopardwithDTrace/index.html)
318* [Big Nerd Ranch's four-part series](https://www.bignerdranch.com/blog/hooked-on-dtrace-part-1/)
319* [Defining static probes on macOS](http://www.macresearch.org/tuning-cocoa-applications-using-dtrace-custom-static-probes-and-instruments)
320* [Examples](http://www.brendangregg.com/dtrace.html#Examples)
321* [Tips from Sun](http://blogs.sun.com/bmc/resource/dtrace_tips.pdf)
322
323DTrace examples on macOS: `/usr/share/examples/DTTk`
324
325To get truss on macOS, use dtruss. That requires root, so use `sudo dtruss -p`
326and to attach to a running non-root program.
327
328## Memory/Heap Inspection
329
330Chrome has [built-in memory instrumentation](../memory-infra/README.md) that can
331be used to identify allocations and potential leaks.
332
333MacOS also provides several low-level command-line tools that can be used to
334inspect what's going on with memory inside a process.
335
336**`heap`** summarizes what's currently in the malloc heap(s) of a process. (It
337only works with regular malloc, of course, but Mac Chrome still uses that.) It
338shows a number of useful things:
339
340* How much of the heap is used or free
341* The distribution of block sizes
342* A listing of every C++, Objective-C and CoreFoundation class found in the
343 heap, with the number of instances, total size and average size.
344
345It identifies C++ objects by their vtables, so it can't identify vtable-less
346classes, including a lot of the lower-level WebCore ones like StringImpl. To
347work around, temporarily added the `virtual` keyword to `WTF::RefCounted`'s
348destructor method, which forces every ref-counted object to include a vtable
349pointer identifying its class.
350
351**`malloc_history`** identifies the stack backtrace that allocated every malloc
352block in the heap. It lists every unique backtrace together with its number of
353blocks and their total size. It requires that the process use malloc stack
354logging, which is enabled if the environment variable MallocStackLogging is set
355when it launches. The `env` command is handy for this:
356
357 $ env MallocStackLogging=1 Chromium.app/Contents/MacOS/Chromium
358
359Then in another shell you run
360
361 $ malloc_history <pid> -all_by_size
362
363Watch out: the output is *big*.
364
365**`leaks`** finds malloc blocks that have no pointers to them and are probably
366leaked. It doesn't require MallocStackLogging, but it's more useful if it's on
367because it can then show the backtrace that allocated each leaked block.
368
369**`vmmap`** shows all the virtual-memory regions in the process's address space.
370This is less useful since it doesn't say anything about individual malloc blocks
371(except huge ones) but it can be useful for looking at things like static data
372size, mapped files, and how much memory is paged out. The "-resident" flag shows
373how much of each allocation is currently paged into RAM. See the man page for
374details.
375
376Notes:
377
378* These are not going to be very useful on stripped binaries, and they're less
379 useful in release builds.
380* All of these except vmmap take several *minutes* to run, apparently because
381 of the number of symbols in Chrome. They spend most of their time pegging
382 one CPU down inside system code that's reading symbol tables from the
383 binary. Be patient.
384* Instruments is an application bundled with Xcode that provides GUI interfaces
385 for many of these tools, including `sample` and `leaks`. Most Chromies prefer
386 the command line tools, but Instruments can be useful. If running Instruments
387 on a local build, expect to wait a few minutes for it to load symbols before
388 it starts recording useful data
389
390## Resources
391
392The [Mac OS X Debugging Magic
393Technote](https://developer.apple.com/technotes/tn2004/tn2124.html) contains a
394wealth of (mostly outdated) information about various debugging options built in
395to macOS.