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
命令提供当前文件的信息:
info shared
命令在虚拟地址空间中打印动态库列表机的地址,启动时加载到该地址,以便程序运行:
假如你想知道 Linux 请参阅我的文章 在 Linux 如何处理动态库和静态库。
调试程序你可能已经注意到你可以了 GDB 中使用run
命令启动程序。调试程序
你可能已经注意到你可以了 GDB 中使用
run
命令启动程序。run
命令接受命令行参数,就像从控制台启动程序一样。
run命令。如果没有
-c1开关,程序将陷入死循环,您必须使用它
Ctrl C结束死循环。
main函数。使用
list main命令打开显示器
main
main
函数在第 33 所以可以输入break 33
在 33 行加断点:
run
运行程序。正如预期的那样,程序在main停止函数。输入
layout src并查看源代码:
你现在处于 GDB 文本用户界面(TUI)模式。用键盘向上和向下的箭头键滚动查看源代码。
GDB 高亮显示当前执行。你可以输入next(
n
有时,你会发现文本的输出有点异常:
如果发生这种情况,请按下Ctrl L
重置屏幕。
使用
Ctrl X A可随时进出 TUI 模式。您可以在手册中找到其他键绑定。要退出 GDB,只需输入quit
。
设置监察点
该示例程序的核心是在无限循环中运行的状态机。n_state
变量列举了所有当前状态:while(true){ switch(n_state){ case State_1: std::cout
假如你想做
的值为State_5
停止程序。为此,请在main
停止程序并为函数
n_state设置监督点:
在当前上下文中使用所需的变量时,使用变量名设置监测点是有效的。
在继续运行程序时,您将获得以下输出:
如果您继续操作程序,当监控点表达式评估为false时 GDB 将停止:当一般值变化、特定值、读取或写入时,可以设置监测点。
info watchpoints打印以前设置的监控点列表:
删除断点和监控点如你所见,监察点是数字。改变断点和监督点
输入info watchpoints
打印以前设置的监控点列表:
删除断点和监控点
如你所见,监控点是数字。要删除特定的监控点,请先输入
输入监控点号后。例如,我的监察点编号是 2.删除此监控点,输入
delete 2。注意:
如果你使用
所有删除监测点和断点。
也适用于断点。在下面的截屏中,我添加了几个断点,输入
info breakpoint
输入断点号。另一种方法:您可以通过指定断点的行号删除断点。例如,clear 78
命令将删除第 78 行设置的断点号 7。禁止或启用断点和监督点
除了删除断点或监控点外,还可以输入
,输入禁用断点或监控点。断点在下面 3 和 4 禁用,并在代码窗口用减号标记:输入也可以相似
disable 2 - 4
enable,然后输入它们的号码。
首先,输入
delete
main在函数处,如果不想指定行号,可以直接指示函数添加断点。输入
break main从而在main
在函数处添加断点。输入
程序将从头开始运行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
你可以在手册中找到一整章 断点、监控点和捕捉点的内容。评估和操作符号
用print
命令可以打印变量值。一般语法是
。请输入修改变量值:set variable
n_state_3_count的值设为
123
&
您可以在虚拟地址空间中打印地址。如果不确定符号的数据类型,可以使用whatis来查明。若要列出
main请输入函数范围内可用的所有变量
info scope main
值是指基于当前子程序的堆栈偏移量。或者,如果你已经在一个函数中列出了当前堆栈帧上的所有变量,你可以使用它info locals
:查看手册了解更多 检查符号的内容。
附加调试到正在运行的过程
gdb attach
命令允许您指定过程 ID(PID)附加到已经在运行过程中进行调试。幸运的是,coredump
目前的程序 PID 打印到屏
幕上