断点会在执行某些代码时停止执行。如需创建断点,请使用 break
命令(简称 b
)并为其指定位置:
[zxdb] break main
Breakpoint 3 (Software) on Global, Enabled, stop=All, @ main
180
◉ 181 int main(int argc, char**argv) {
182 fbl::unique_fd dirfd;
位置可以用多种不同的方式表示。
简单的函数名称。这会将函数与任何命名空间中的名称进行匹配:
[zxdb] break main
成员函数或特定命名空间或类中的函数:
[zxdb] break my_namespace::MyClass::MyFunction [zxdb] break ::OtherFunction
源文件 + 行号(以冒号分隔):
[zxdb] break mymain.cc:22
当前帧的当前源文件中的行号(在步进时很有用):
[zxdb] break 23
内存地址:
[zxdb] break 0xf72419a01
表达式:添加“*”前缀会将以下输入视为计算结果为地址的表达式。这种方式最常与硬件断点一起使用。
[zxdb] break --type=write *&foo
如需列出所有断点,请运行以下命令:
[zxdb] breakpoint
如需清除特定断点,请提供该断点索引作为清除命令的上下文(请参阅上面的“交互模型”)。以下是我们使用的 breakpoint
(bp
) 的缩写:
[zxdb] bp 2 clear
或者,您也可以清除当前断点:
[zxdb] clear
每当您创建或停止某个断点时,该断点都会自动成为默认断点,因此 clear
始终会清除您刚刚遇到的断点。
clear
也可以接受可选位置,就像 break
命令一样。这样一来,它会尝试清除该位置的所有断点,并忽略默认的断点上下文。
GDB 用户注意事项:
delete <index>
会映射到bp <index> clear
,而clear <number>
在 GDB 和 zxdb 中的行为相同。
您也可以启用或停用断点:
[zxdb] disable
[zxdb] bp 4 enable
其他属性可通过“get”和“set”命令进行修改。
[zxdb] bp 1 set location = Frobulator::GetThing
条件断点
断点可以有条件,该条件是计算结果为 true 或 false 的表达式。除非条件为 true,否则断点不会触发停止。例如,假设某个源文件
7 void do_loop(int n) {
8 for (int i = 0; i < n; i++) {
▶ 9 std::cout << "Hello world!" << std::endl;
10 }
11 }
可以设置一个仅在最后一次迭代时停止的断点。
b 9 if i == n - 1
硬件数据断点(“监视点”)
可以对处理器进行设置,使其在读取或写入特定地址时中断执行。这对于跟踪内存损坏特别有用。通过在断点命令的“类型”中指定“写入”“执行”或“读写”来创建硬件断点(与其他某些调试程序不同,硬件断点公开为断点类型,而不是单独的“监视点”概念)。
[zxdb] break --type=read-write --size=4 0x12345670
作为一种快捷方式,“watch”命令将获取变量的内容或表达式的结果,并在其范围内设置数据写入断点:
[zxdb] watch i
[zxdb] watch foo[5]->bar
注意:
CPU 仅支持有限数量的硬件监视点,通常约为 4 个。
监视点范围的大小上限为 1、2、4 或 8 个字节,并且地址大小必须是该大小的偶数倍。
与 GDB 不同,“watch”会对表达式求值一次,并在结果中设置一个断点。而不会重新评估表达式。在上面的示例中,它会在“bar”发生变化时触发,但在
foo[5]
更改为指向其他“bar”时则不会触发。如果您观察到堆栈上的某个变量且无人触及该变量,则经常会看到当重复使用堆栈内存时,该变量在程序的其他部分命中。如果您遇到意外的断点命中,请检查执行是否仍在预期帧中。
程序化断点
如果您想捕获某些特定条件,可以在代码中插入硬编码断点。Clang 具有内置代码(在 GCC Zircon 版本中无法运行):
__builtin_debugtrap();
如果调试程序已连接到进程,它将像遇到正常断点一样停止。您可以从该步骤中继续操作,也可以继续操作。如果尚未连接调试程序,将导致崩溃。