在 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, you can enable step-through debugging with breakpoints inside Electron’s source code. You can also use XCode for debugging if you prefer a graphical interface.

要求

  • A testing build of Electron: The easiest way is usually to build it from source, which you can do by following the instructions in the build instructions. While you can attach to and debug Electron as you can download it directly, you will find that it is heavily optimized, making debugging substantially more difficult. In this case 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, you should also install the Xcode command line tools. They include LLDB, the default debugger in Xcode on macOS. It supports debugging C, Objective-C and C++ on the desktop and iOS devices and simulator.

  • .lldbinit: Create or edit ~/.lldbinit to allow Chromium code to be properly source-mapped.

    1. # e.g: ['~/electron/src/tools/lldb']
    2. script sys.path[:0] = ['<...path/to/electron/src/tools/lldb>']
    3. script import lldbinit

附加并调试 Electron

To start a debugging session, open up Terminal and start lldb, passing a non-release build of Electron as a parameter.

  1. $ lldb ./out/Testing/Electron.app
  2. (lldb) target create "./out/Testing/Electron.app"
  3. Current executable set to './out/Testing/Electron.app' (x86_64).

设置断点

LLDB是一个强大的工具,支持进行多种策略的代码检查。 在这做一个基本的介绍,让我们假设你从 JavaScript 调用一个不正常的命令 - 所以你想打断该命令的 C++ 对应的 Electron 源。

Relevant code files can be found in ./shell/.

让我们假设你想调试 app.setName(), 在 browser.cc 中定义为 Browser::SetName(). 使用 breakpoint 命令进行断点,指定文件和断点位置:

  1. (lldb) breakpoint set --file browser.cc --line 117
  2. Breakpoint 1: where = Electron Framework`atom::Browser::SetName(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&) + 20 at browser.cc:118, address = 0x000000000015fdb4

然后, 启动 Electron:

  1. (lldb) run

应用程式会立即暂停,因为 Electron 会在启动时设定应用程序名称:

  1. (lldb) run
  2. Process 25244 launched: '/Users/fr/Code/electron/out/Testing/Electron.app/Contents/MacOS/Electron' (x86_64)
  3. Process 25244 stopped
  4. * 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
  5. frame #0: 0x0000000100162db4 Electron Framework`atom::Browser::SetName(this=0x0000000108b14f20, name="Electron") + 20 at browser.cc:118
  6. 115 }
  7. 116
  8. 117 void Browser::SetName(const std::string& name) {
  9. -> 118 name_override_ = name;
  10. 119 }
  11. 120
  12. 121 int Browser::GetBadgeCount() {
  13. (lldb)

显示当前帧的参数和局部变量, 运行 frame variable (或 fr v), 这将显示你的应用程序当前设置名称为 “Electron”.

  1. (lldb) frame variable
  2. (atom::Browser *) this = 0x0000000108b14f20
  3. (const string &) name = "Electron": {
  4. [...]
  5. }

在当前选择的线程中执行源级单步执行, 执行 step (或 s). 这将带你进入 name_override_.empty()。 继续前进,步过,运行 next (或 n).

  1. (lldb) step
  2. Process 25244 stopped
  3. * thread #1: tid = 0x839a4c, 0x0000000100162dcc Electron Framework`atom::Browser::SetName(
  4. this=0x0000000108b14f20, name="Electron"
  5. ) + 44 at browser.cc:119, queue = 'com.apple.main-thread', stop reason = step in
  6. frame #0: 0x0000000100162dcc Electron Framework`atom::Browser::SetName(
  7. this=0x0000000108b14f20, name="Electron"
  8. ) + 44 at browser.cc:119
  9. 116
  10. 117 void Browser::SetName(const std::string& name) {
  11. 118 name_override_ = name;
  12. -> 119 }
  13. 120
  14. 121 int Browser::GetBadgeCount() {
  15. 122 return badge_count_;

NOTE: If you don’t see source code when you think you should, you may not have added the ~/.lldbinit file above.

要完成此时的调试,运行 process continue。 你也可以继续,直到这个线程中的某一行被命中(线程直到100)。 此命令将在当前帧中运行线程,直到它到达此帧中的行100,或者如果它离开当前帧,则停止。

现在,如果你打开 Electron 的开发工具并调用 setName,你将再次命中断点。

进一步阅读

LLDB是一个强大的工具,有一个庞大的文档。 To learn more about it, consider Apple’s debugging documentation, for instance the LLDB Command Structure Reference or the introduction to Using LLDB as a Standalone Debugger.

You can also check out LLDB’s fantastic manual and tutorial, which will explain more complex debugging scenarios.