Mac OS X Debugging Magic Technote contains a wealth of information about various debugging options built in to OS X.
IMPORTANT: By default, Xcode has the "Load Symbols Lazily" preference set. As a result, any symbols not in the main static library (99% of our code) won't be visible to set breakpoints. The result is that you set breakpoints in the editor window and they're ignored entirely when you run. The fix, however, is very simple! Uncheck the "Load Symbols Lazily" checkbox in the "Debugging" panel in preferences. Now all your breakpoints will work, at the expense of a little longer load time in gdb. Well worth it, if you ask me.
Mac OS X helpfully tries to write a crash report every time a binary crashes – which happens for example when a test in unit_tests fails. Since Chromium's debug binaries are huge, this takes forever. If this happens, "ReportCrash" will be the top cpu consuming process in Activity Monitor. You should disable ReportCrash while you work on Chromium. Run man ReportCrash to learn how to do this on your version of OS X. On 10.8, the command is
If you get a Google Chrome crash report caught by ReportCrash/OS X, it will not have symbols (every frame will be ChromeMain). To get a symbolized stack trace, use the internal crsym tool by simply pasting the contents of an entire Apple crash report.
From now on Chromium will launch in single-process mode when invoked through this Xcode project, and the debugger will work fine. This obviously changes the apps behavior slightly, but for most purposes the differences aren't significant. If they are, though, you'll need to…
1. Launch the main executable from the Terminal (not through XCode) and pass in the --renderer-startup-dialog flag on the command line. On OS X this causes the renderer to print a message with its PID and then call pause() immediately up on startup. This has the effect of pausing execution of the renderer until the process receives a signal (such as attaching the debugger).
$ ~/dev/chrome//src/xcodebuild/Debug/Chromium.app/Contents/MacOS/Chromium --renderer-startup-dialog
the output should look like:
[33215:2055:244180145280185:WARNING:/Users/Shared/bla/chrome/src/chrome/renderer/renderer_main.cc(48)] Renderer (33215) paused waiting for debugger to attach @ pid
So 33215 is the PID of the renderer process in question.
2. Open chrome.xcodeproj in XCode and select Run -> Attach To Process -> Process ID ..
3. Click OK and off you go...
Similar to debugging the renderer process, simply attaching gdb to a out-of-process test like browser_tests will not hit the test code. In order to debug a browser test, you need to run the test binary with "--single_process" (note the underscore in single_process). Because you can only run one browser test in the same process, you're probably going to need to add --gtest_filter as well. So your command will look like this:
/path/to/src/xcodebuild/Debug/browser_tests --single_process --gtest_filter=GoatTeleporterTest.DontTeleportSheephttps://sites.google.com/a/chromium.org/dev/developers/f-script-anywhere for more information.
See the instructions that discuss that scenario.
Disabling the sandbox can sometimes be useful when debugging, this can be achieved by passing the --no-sandbox flag on the command line. This will, for example, allow writing out debugging information to a file from the Renderer Process.
$ ~/dev/chrome//src/xcodebuild/Debug/Chromium.app/Contents/MacOS/Chromium --no-sandbox
Launch chrome with the --enable-sandbox-logging flag. This will cause a message to be printed to /var/log/system.log every time an operation is denied by the Sandbox (you can use Console.app to watch logfiles). This is really useful for debugging and can often provide an explanation for very puzzling problems.
You can also get the Sandbox to send a SIGSTOP to a process when the sandbox denies functionality. This allows you to attach with a debugger and continue the execution from where it left off:
If a breakpoint you set isn't causing the debugger to stop, try one of these solutions:
Profiling tools like Shark and 'sample' expect to find symbol names in the binary, but in Release builds most symbols are stripped out. You can preserve symbols by temporarily changing the build process, by adding mac_strip_release=0 to your GYP_DEFINES, rerunning gclient runhooks, and rebuilding (changing this define only relinks the main binary, it doesn't recompile everything).
(The above "Debugging in Release Mode" trick with the .dSYM file might work for Shark/sample too; I haven't tried it yet. —snej)
Defining static probes on OS X:
DTrace examples on OS X: /usr/share/examples/DTTk
To get truss on Mac OS X, use dtruss. That requires root, so I often sudo dtruss -p and attach to a running nonroot program.
To test Chrome in a different locale, change your system locale via the System Preferences. (Keep the preferences window open so that you can change the locale back without needing to navigate through menus in a language you may not know.)
There are several low-level command-line tools that can be used to inspect what's going on with memory inside a process.
'heap' summarizes what's currently in the malloc heap(s) of a process. (It only works with regular malloc, of course, but Mac Chrome still uses that.) It shows a number of useful things:
'malloc_history' identifies the stack backtrace that allocated every malloc block in the heap. It lists every unique backtrace together with its number of blocks and their total size. It requires that the process use malloc stack logging, which is enabled if the environment variable MallocStackLogging is set when it launches. The 'env' command is handy for this:
$ env MallocStackLogging=1 Chromium.app/Contents/MacOS/ChromiumThen in another shell you run
$ malloc_history pid -all_by_sizeWatch out: the output is big. I ran malloc_history on a fairly bloated heap and got 60MB of text.
'leaks' finds malloc blocks that have no pointers to them and are probably leaked. It doesn't require MallocStackLogging, but it's more useful if it's on because it can then show the backtrace that allocated each leaked block. (So far I've seen only trivial leakage in Chrome.)
'vmmap' shows all the virtual-memory regions in the process's address space. This is less useful since it doesn't say anything about individual malloc blocks (except huge ones) but it can be useful for looking at things like static data size, mapped files, and how much memory is paged out. I recommend the "-resident" flag, which shows how much of each allocation is currently paged into RAM. See the man page for details.
If you are looking at a crash report that ends in CrMallocErrorBreak, then either a malloc or free call has failed with the given stacktrace. Chromium overrides the empty function malloc_error_break in OS X's Libc with CrMallocErrorBreak. The system calls this function as a debugging aide that we've made fatal because it catches useful memory errors. Specifically, CrMallocErrorBreak will be called (resulting in a crash) under the following circumstances:
If you get a crash report that that ends in CrMallocErrorBreak, it is likely not an issue with this feature. It is instead surfacing a (sometimes serious) bug in your code or other code that is stomping on your code's memory. Using Chromium's memory tools (ASan, HeapCheck, and Valgrind) is a good start, if you can reproduce the problem.
Enabling high-DPI (aka "HiDPI" or "Retina") modes on standard-DPI hardware.
Under Mac OS X 10.7 and above it's possible to fake "HiDPI" modes on standard-DPI hardware. This can be useful in testing up-scaling codepaths or high-DPI resources.