From ab69ae07b70180547e0b9ef38e66254facaf118e Mon Sep 17 00:00:00 2001 From: Felix Rieseberg Date: Thu, 28 Jul 2016 12:54:03 -0700 Subject: [PATCH] :memo: Add debugging instructions for macOS MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit I already wrote up debugging instructions for Windows, but never got around to writing them for macOS - until now! This adds a very basic introduction to LLDB from the command line, which should empower people to figure out what’s happening inside Electron when they call an Electron method from JavaScript. --- docs/README.md | 3 +- .../debugging-instructions-macos.md | 125 ++++++++++++++++++ 2 files changed, 127 insertions(+), 1 deletion(-) create mode 100644 docs/development/debugging-instructions-macos.md diff --git a/docs/README.md b/docs/README.md index 9f4973759453..aec23bf47ec1 100644 --- a/docs/README.md +++ b/docs/README.md @@ -93,5 +93,6 @@ an issue: * [Build Instructions (macOS)](development/build-instructions-osx.md) * [Build Instructions (Windows)](development/build-instructions-windows.md) * [Build Instructions (Linux)](development/build-instructions-linux.md) -* [Debug Instructions (Windows)](development/debug-instructions-windows.md) +* [Debug Instructions (macOS)](development/debug-instructions-windows.md) +* [Debug Instructions (Windows)](development/debug-instructions-macos.md) * [Setting Up Symbol Server in debugger](development/setting-up-symbol-server.md) diff --git a/docs/development/debugging-instructions-macos.md b/docs/development/debugging-instructions-macos.md new file mode 100644 index 000000000000..e119db466a19 --- /dev/null +++ b/docs/development/debugging-instructions-macos.md @@ -0,0 +1,125 @@ +# Debugging on macOS + +If you experience crashes or issues in Electron that you believe are not caused +by your JavaScript application, but instead by Electron itself, debugging can +be a little bit tricky, especially for developers not used to native/C++ +debugging. However, using lldb, and the Electron source code, it is fairly easy +to enable step-through debugging with breakpoints inside Electron's source code. + +## Requirements + +* **A debug build of Electron**: The easiest way is usually building it + yourself, using the tools and prerequisites listed in the + [build instructions for macOS](build-instructions-osx.md). While you can + easily attach to and debug Electron as you can download it directly, you will + find that it is heavily optimized, making debugging substantially more + difficult: The debugger will not be able to show you the content of all + variables and the execution path can seem strange because of inlining, + tail calls, and other compiler optimizations. + +* **Xcode**: In addition to Xcode, also install the Xcode command line tools. + They include LLDB, the default debugger in Xcode on Mac OS X. It supports + debugging C, Objective-C and C++ on the desktop and iOS devices and simulator. + +## Attaching to and Debugging Electron + +To start a debugging session, open up Terminal and start `lldb`, passing a debug +build of Electron as a parameter. + +```bash +$ lldb ./out/D/Electron.app +(lldb) target create "./out/D/Electron.app" +Current executable set to './out/D/Electron.app' (x86_64). +``` + +### Setting Breakpoints + +LLDB is a powerful tool and supports multiple strategies for code inspection. For +this basic introduction, let's assume that you're calling a command from JavaScript +that isn't behaving correctly - so you'd like to break on that command's C++ +counterpart inside the Electron source. + +Relevant code files can be found in `./atom/` as well as in Brightray, found in +`./vendor/brightray/browser` and `./vendor/brightray/common`. If you're hardcore, +you can also debug Chromium directly, which is obviously found in `chromium_src`. + +Let's assume that you want to debug `app.setName()`, which is defined in `browser.cc` +as `Browser::SetName()`. Set the breakpoint using the `breakpoint` command, specifying +file and line to break on: + +```bash +(lldb) breakpoint set --file browser.cc --line 117 +Breakpoint 1: where = Electron Framework`atom::Browser::SetName(std::__1::basic_string, std::__1::allocator > const&) + 20 at browser.cc:118, address = 0x000000000015fdb4 +``` + +Then, start Electron: + +```bash +(lldb) run +``` + +The app will immediately be paused, since Electron sets the app's name on launch: + +```bash +(lldb) run +Process 25244 launched: '/Users/fr/Code/electron/out/D/Electron.app/Contents/MacOS/Electron' (x86_64) +Process 25244 stopped +* thread #1: tid = 0x839a4c, 0x0000000100162db4 Electron Framework`atom::Browser::SetName(this=0x0000000108b14f20, name="Electron") + 20 at browser.cc:118, queue = 'com.apple.main-thread', stop reason = breakpoint 1.1 + frame #0: 0x0000000100162db4 Electron Framework`atom::Browser::SetName(this=0x0000000108b14f20, name="Electron") + 20 at browser.cc:118 + 115 } + 116 + 117 void Browser::SetName(const std::string& name) { +-> 118 name_override_ = name; + 119 } + 120 + 121 int Browser::GetBadgeCount() { +(lldb) +``` + +To show the arguments and local variables for the current frame, run `frame variable` (or `fr v`), +which will show you that the app is currently setting the name to "Electron". + +```bash +(lldb) frame variable +(atom::Browser *) this = 0x0000000108b14f20 +(const string &) name = "Electron": { + [...] +} +``` + +To do a source level single step in the currently selected thread, execute `step` (or `s`). +This would take you into into `name_override_.empty()`. To proceed and do a step over, +run `next` (or `n`). + +```bash +(lldb) step +Process 25244 stopped +* thread #1: tid = 0x839a4c, 0x0000000100162dcc Electron Framework`atom::Browser::SetName(this=0x0000000108b14f20, name="Electron") + 44 at browser.cc:119, queue = 'com.apple.main-thread', stop reason = step in + frame #0: 0x0000000100162dcc Electron Framework`atom::Browser::SetName(this=0x0000000108b14f20, name="Electron") + 44 at browser.cc:119 + 116 + 117 void Browser::SetName(const std::string& name) { + 118 name_override_ = name; +-> 119 } + 120 + 121 int Browser::GetBadgeCount() { + 122 return badge_count_; +``` + +To finish debugging at this point, run `process continue`. You can also continue until a certain +line is hit in this thread (`thread until 100`). This command will run the thread in the current +frame till it reaches line 100 in this frame or stops if it leaves the current frame. + +Now, if you open up Electron's developer tools and call `setName`, you will once again hit the +breakpoint. + +### Further Reading +LLDB is a powerful tool with a great documentation. To learn more about it, consider +Apple's debugging documentation, for instance the [LLDB Command Structure Reference][lldb-command-structure] +or the introduction to [Using LLDB as a Standalone Debugger][lldb-standalone]. + +You can also check out LLDB's fantastic [manual and tutorial][lldb-tutorial], which +will explain more complex debugging scenarios. + +[lldb-command-structure]: https://developer.apple.com/library/mac/documentation/IDEs/Conceptual/gdb_to_lldb_transition_guide/document/lldb-basics.html#//apple_ref/doc/uid/TP40012917-CH2-SW2 +[lldb-standalone]: https://developer.apple.com/library/mac/documentation/IDEs/Conceptual/gdb_to_lldb_transition_guide/document/lldb-terminal-workflow-tutorial.html +[lldb-tutorial]: http://lldb.llvm.org/tutorial.html