win764旗舰版使用技巧
()
GNU 调试器是发现程序缺陷的有力工具。
如果你是程序员,想在你的软件中添加一些功能,你首先考虑实现它的方法:比如写一个方法,定义一个类,或者创建一个新的数据类型。然后你使用编译器或解释器可以理解的编程语言来实现这个功能。但是,如果你认为你所有的代码都是正确的,但是编译器或解释器仍然不能理解你的指令呢?如果软件在大多数情况下运行良好,但在某些环境下有缺陷呢?在这种情况下,你必须知道如何正确使用调试器来找到问题的根源。
GNU 调试器GNU Project Debugger(GDB)是发现项目缺陷的有力工具。它通过跟踪程序运行过程中发生的事情来帮助你发现程序错误或崩溃的原因。(LCTT 校注:GDB 全程是“GNU Project Debugger”,即 “GNU 项目调试器”,但是通常我们简称为“GNU 调试器”)
本文是 GDB 实践教程的基本用法。请遵循示例,打开命令线,克隆仓库:
git clone https://github.com/hANSIc99/core_dump_example.git
快捷方式GDB 每一个命令都可以缩短。例如,显示设定的断点info break
命令可以缩短为i break
。你可能在其他地方看到过这个缩写,但在这篇文章中,为了清楚地显示使用的函数,我写了整个命令。
命令行参数你可以将 GDB 附加到每个可执行文件中。进入你克隆的仓库(core_dump_example
),运行make
进行编译。现在你可以看到一个名字coredump
的可执行文件。(更多信息,请参考我的文章《创建与调试》 Linux 转储文件。)
要将 GDB 请输入此可执行文件:gdb coredump
。
你的输出应如下所示:
调试符号未找到返回结果。
调试信息是目标文件object file调试信息包括数据类型、函数签名、源代码和操作代码之间的关系。此时,您有两种选择:
使用调试信息编译继续调试汇编码(见下面的无符号调试)
编译调试信息您必须重新编译二进制文件中的调试信息。打开Makefile
,删除第 9 注释标签(#
)重新编译:
CFLAGS =-Wall -Werror -std=c 11 -g
-g
告知编译器包含调试信息。运行make clean
,接着运行make
,然后再调用 GDB。您可以在输出后调试代码:
新增的调试信息会增加可执行文件的大小。在这种情况下,执行文件增加了 2.5 倍(从 26,088 字节 增加到 65,480 字节)。
输入run -c1
,使用-c1
开关启动程序。当程序运行到达时State_4
时将崩溃:
您可以检索有关程序的其他信息,info source
命令提供当前文件的信息:
101 行代码语言: C 调试格式的编译器(版本、调优、架构、调试标志、语言标准):DWARF 没有预处理器宏指令(使用) GCC 宏只在编译时 使用 -g3 可用于标志编译)。
info shared
命令在虚拟地址空间中打印动态库列表机的地址,启动时加载到该地址,以便程序运行:
假如你想知道 Linux 请参阅我的文章 在 Linux 如何处理动态库和静态库。
调试程序你可能已经注意到你可以了 GDB 中使用run
命令启动程序。
调试程序
你可能已经注意到你可以了 GDB 中使用run
命令启动程序。run
命令接受命令行参数,就像从控制台启动程序一样。
-c1
开关会导致程序第一 4 阶段崩溃。从头开始操作程序,不用退出 GDB,只需再次操作
run命令。如果没有
-c1开关,程序将陷入死循环,您必须使用它
Ctrl C结束死循环。
您还可以一步一步地操作程序。在 C/C 中,入口是
main函数。使用
list main命令打开显示器
main
函数的部分源代码:
main
函数在第 33 所以可以输入break 33
在 33 行加断点:
输入
run
运行程序。正如预期的那样,程序在
main停止函数。输入
layout src并查看源代码:
你现在处于 GDB 文本用户界面(TUI)模式。用键盘向上和向下的箭头键滚动查看源代码。
GDB 高亮显示当前执行。你可以输入
next(
n
)命令逐行执行。若未指定新命令,GBD 会执行上一个命令。只需按操作代码,只需按回车键即可。
有时,你会发现文本的输出有点异常:
如果发生这种情况,请按下Ctrl L
重置屏幕。
使用
Ctrl X A
可随时进出 TUI 模式。您可以在手册中找到其他键绑定。要退出 GDB,只需输入quit
。
设置监察点
该示例程序的核心是在无限循环中运行的状态机。n_state
变量列举了所有当前状态:
while(true){ switch(n_state){ case State_1: std::cout
假如你想做
n_state
的值为State_5
停止程序。为此,请在main
停止程序并为函数
n_state
设置监督点:
watch n_state == State_5
在当前上下文中使用所需的变量时,使用变量名设置监测点是有效的。
当你输入
continue
在继续运行程序时,您将获得以下输出:
如果您继续操作程序,当监控点表达式评估为
false时 GDB 将停止:
当一般值变化、特定值、读取或写入时,可以设置监测点。
改变断点和监督点
输入
info watchpoints打印以前设置的监控点列表:
删除断点和监控点如你所见,监察点是数字。
改变断点和监督点
输入info watchpoints
打印以前设置的监控点列表:
删除断点和监控点
如你所见,监控点是数字。要删除特定的监控点,请先输入
delete
输入监控点号后。例如,我的监察点编号是 2.删除此监控点,输入
delete 2
。注意:
如果你使用
delete
而没有指定数字,
所有删除监测点和断点。
也适用于断点。在下面的截屏中,我添加了几个断点,输入
info breakpoint
打印断点列表:
要删除单个断点,请先输入delete
输入断点号。另一种方法:您可以通过指定断点的行号删除断点。例如,clear 78
命令将删除第 78 行设置的断点号 7。
禁止或启用断点和监督点
除了删除断点或监控点外,还可以输入
disable
,输入禁用断点或监控点。断点在下面 3 和 4 禁用,并在代码窗口用减号标记:
输入也可以相似
disable 2 - 4
在一定范围内修改断点或监测点。如需重新激活这些点,请输入
enable,然后输入它们的号码。
条件断点
首先,输入
delete
删除所有断点和监控点。您仍然希望程序停止
main在函数处,如果不想指定行号,可以直接指示函数添加断点。输入
break main从而在main
在函数处添加断点。
输入
run
程序将从头开始运行main
停止函数。
main
函数包括变量
n_state_3_count,当状态机达到状态时 3 变量会增加。
基于
n_state_3_count添加条件断点,请输入:
break 54 if n_state_3_count == 3
继续运行程序。程序将在第 54 停止前运行状态机 3 次。要查看n_state_3_count
请输入:
print n_state_3_count
使断点成为条件断点
现有的断点也可以成为条件断点。用
clear 54
命令删除最近添加的断点,并通过输入break 54
命令添加一个简单的断点。您可以输入以下内容,使此断点成为条件断点:
condition 3 n_state_3_count == 9
3
指断点号。
在其他源文件中设置断点
若您的程序由多个源文件组成,则可在行号前指定文件名设置断点,例如,
break main. cpp:54
。
捕捉点
除断点和监控点外,还可设置捕获点。捕获点适用于执行系统调用、加载共享库或引起异常等事件。
用于写入的捕获 STDOUT 的write
请输入系统调用:
catch syscall write
当程序写入控制台输出时,GDB 中断执行。
你可以在手册中找到一整章 断点、监控点和捕捉点的内容。
评估和操作符号
用print
命令可以打印变量值。一般语法是
print
。请输入修改变量值:
set variable
在下面的截屏中,我将变量
n_state_3_count的值设为
123
。
/x
表达式以16进制打印值;使用
&
您可以在虚拟地址空间中打印地址。
如果不确定符号的数据类型,可以使用
whatis来查明。
若要列出
main请输入函数范围内可用的所有变量
info scope main
:
DW_OP_fbreg
值是指基于当前子程序的堆栈偏移量。
或者,如果你已经在一个函数中列出了当前堆栈帧上的所有变量,你可以使用它info locals
:
查看手册了解更多 检查符号的内容。
附加调试到正在运行的过程
gdb attach
命令允许您指定过程 ID(PID)附加到已经在运行过程中进行调试。幸运的是,coredump
目前的程序 PID 打印到屏幕上