在 macOS 上调试
🌐 Debugging on macOS
如果你在 Electron 中遇到崩溃或问题,并且你认为这些问题不是由你的 JavaScript 应用引起的,而是由 Electron 本身引起的,那么调试可能会有些棘手,尤其是对于不熟悉本地 C++ 调试的开发者来说。然而,通过使用 lldb 和 Electron 的源码,你可以在 Electron 的源代码中启用带断点的逐步调试。如果你更喜欢图形界面,也可以使用 XCode 进行调试。
🌐 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.
要求
🌐 Requirements
-
Electron 的测试版本:最简单的方法通常是从源码构建,你可以按照构建说明进行操作。虽然你可以直接下载 Electron 并进行调试,就像附加调试一样,但你会发现它经过了高度优化,使调试变得相当困难。在这种情况下,调试器可能无法显示所有变量的内容,并且由于内联、尾调用和其他编译器优化,执行路径看起来可能会很奇怪。
-
Xcode:除了 Xcode 本身,你还应安装 Xcode 命令行工具。它们包括 LLDB,这是 macOS 上 Xcode 的默认调试器。它支持在桌面和 iOS 设备及模拟器上调试 C、Objective-C 和 C++。
-
.lldbinit:创建或编辑
~/.lldbinit以允许 Chromium 代码正确进行源映射。# e.g: ['~/electron/src/tools/lldb']
script sys.path[:0] = ['<...path/to/electron/src/tools/lldb>']
script import lldbinit
连接并调试 Electron
🌐 Attaching to and Debugging Electron
要开始调试会话,打开终端并启动 lldb,将 Electron 的非发布版本作为参数传入。
🌐 To start a debugging session, open up Terminal and start lldb, passing a non-release
build of Electron as a parameter.
$ lldb ./out/Testing/Electron.app
(lldb) target create "./out/Testing/Electron.app"
Current executable set to './out/Testing/Electron.app' (x86_64).
设置断点
🌐 Setting Breakpoints
LLDB 是一个强大的工具,支持多种代码检查策略。在这个基础介绍中,假设你正在从 JavaScript 调用一个行为异常的命令——因此你希望在 Electron 源代码中该命令的 C++ 对应部分设置断点。
🌐 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.
相关的代码文件可以在 ./shell/ 中找到。
🌐 Relevant code files can be found in ./shell/.
假设你想要调试 app.setName(),它在 browser.cc 中定义为 Browser::SetName()。使用 breakpoint 命令设置断点,指定要断开的文件和行号:
🌐 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:
(lldb) breakpoint set --file browser.cc --line 117
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:
🌐 Then, start Electron:
(lldb) run
该应用将立即暂停,因为 Electron 在启动时设置了应用的名称:
🌐 The app will immediately be paused, since Electron sets the app's name on launch:
(lldb) run
Process 25244 launched: '/Users/fr/Code/electron/out/Testing/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)
要显示当前帧的参数和局部变量,请运行 frame variable(或 fr v),这将向你显示应用当前正在将名称设置为“Electron”。
🌐 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".
(lldb) frame variable
(atom::Browser *) this = 0x0000000108b14f20
(const string &) name = "Electron": {
[...]
}
要在当前选定的线程中执行源级单步,请执行 step(或 s)。这会带你进入 name_override_.empty()。要继续并执行步过操作,请运行 next(或 n)。
🌐 To do a source level single step in the currently selected thread, execute step (or s).
This would take you into name_override_.empty(). To proceed and do a step over,
run next (or n).
(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_;
注意: 如果你认为应该看到源代码却没有看到,可能是因为你没有添加上面的 ~/.lldbinit 文件。
要在此时完成调试,请运行 process continue。你也可以继续运行,直到此线程执行到某一行(thread until 100)。此命令将在当前帧中运行线程,直到它到达当前帧的第100行,或者如果离开当前帧则停止。
🌐 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.
现在,如果你打开 Electron 的开发者工具并调用 setName,你将再次触发断点。
🌐 Now, if you open up Electron's developer tools and call setName, you will once again hit the
breakpoint.
进一步阅读
🌐 Further Reading
LLDB 是一个功能强大的工具,并且有很好的文档。想要了解更多相关信息,可以参考苹果的调试文档,例如 LLDB 命令结构参考 或 将 LLDB 用作独立调试器 的简介。
🌐 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 or the introduction to Using LLDB as a Standalone Debugger.
你也可以看看 LLDB 的精彩 手册和教程,它将解释更复杂的调试场景。
🌐 You can also check out LLDB's fantastic manual and tutorial, which will explain more complex debugging scenarios.