📄 使用 gdb 调试 linux 软件.htm
字号:
<TD bgColor=#588fc7><SPAN class=normalfont><IMG
hspace=2 src="使用 GDB 调试 Linux 软件.files/expand.gif"
align=absMiddle vspace=2><B><A
href="http://www.linuxeden.com/doc/sort.php/14">中文HOWTOs</A></B></SPAN>
</TD></TR></TBODY></TABLE></TD></TR>
<TR id=root>
<TD>
<TABLE cellSpacing=1 cellPadding=0 width="100%"
bgColor=#ffffff border=0>
<TBODY>
<TR>
<TD bgColor=#588fc7><SPAN class=normalfont><IMG
hspace=2 src="使用 GDB 调试 Linux 软件.files/expand.gif"
align=absMiddle vspace=2><B><A
href="http://www.linuxeden.com/doc/sort.php/16">多媒体应用</A></B></SPAN>
</TD></TR></TBODY></TABLE></TD></TR>
<TR id=root>
<TD>
<TABLE cellSpacing=1 cellPadding=0 width="100%"
bgColor=#ffffff border=0>
<TBODY>
<TR>
<TD bgColor=#588fc7><SPAN class=normalfont><IMG
hspace=2 src="使用 GDB 调试 Linux 软件.files/expand.gif"
align=absMiddle vspace=2><B><A
href="http://www.linuxeden.com/doc/sort.php/15">中文化</A></B></SPAN>
</TD></TR></TBODY></TABLE></TD></TR></TBODY></TABLE></TD></TR></TBODY></TABLE></TD>
<TD vAlign=top width=1 bgColor=#bcbcbc></TD>
<TD vAlign=top align=middle>
<TABLE cellSpacing=4 cellPadding=0 width="100%" bgColor=#eeeeee
border=0>
<TBODY>
<TR>
<TD><SPAN class=normalfont><A
href="http://www.linuxeden.com/doc/index.php">Linuxeden.com---自由文档</A>
/ <A href="http://www.linuxeden.com/doc/sort.php/10">编程开发</A>
/ 使用 GDB 调试 Linux 软件</SPAN> </TD></TR></TBODY></TABLE>
<TABLE cellSpacing=0 cellPadding=5 width="100%" border=0>
<TBODY>
<TR>
<TD colSpan=2>
<TABLE cellSpacing=0 cellPadding=4 width="100%" border=0>
<TBODY>
<TR>
<TD vAlign=top align=middle colSpan=2><FONT
class=bigfont><B>使用 GDB 调试 Linux 软件</B>
<FORM name=""
action=http://www.linuxeden.com/doc/modify.php?action=modprofile
method=post></FONT>
<TD vAlign=top borderColor=#c0c0c0 align=left width=71>
<DIV class=edit_article><INPUT type=hidden value=19619
name=articleid> <INPUT type=hidden name=articletitle>
<INPUT type=hidden value=otto name=articleauthor> <INPUT
type=hidden value=modprofile name=action> <INPUT
class=button type=image height=19 width=68
src="使用 GDB 调试 Linux 软件.files/edit.gif" border=0>
</DIV></TD></FORM></FONT></TD></TR>
<TR>
<TD vAlign=top align=middle colSpan=2><SPAN
class=normalfont><FONT color=#666666>2001-06-10</FONT>
otto 点击:
4368</SPAN></TD></TR>
<TR>
<TD vAlign=top bgColor=#eeeeee colSpan=2 height=1></TD></TR>
<TR>
<TD vAlign=top colSpan=2>
<TABLE cellSpacing=1 cellPadding=4 width="100%"
border=0>
<TBODY>
<TR>
<TD vAlign=top>
<DIV class=subhead><B></B></DIV></TD></TR>
<TR>
<TD vAlign=top>
<DIV class=content>
<TABLE style="MARGIN: 10px 7px 3px 4px"
cellSpacing=0 cellPadding=0 align=right
border=0><TBODY>
<TR>
<TD><IFRAME marginWidth=0 marginHeight=0
src="使用 GDB 调试 Linux 软件.files/linux_160_600.html"
frameBorder=0 width=160 scrolling=no
height=600></IFRAME></TD></TR></TBODY></TABLE>Linux
的大部分特色源自于 shell 的 GNU 调试器,也称作 gdb。gdb 可以让您查看程
<BR>序的内部结构、打印变量值、设置断点,以及单步调试源代码。它是功能极其强大的工
<BR>具,适用于修复程序代码中的问题。在本文中,我将尝试说明 gdb 有多棒,多实用。
<BR>编译 <BR>开始调试之前,必须用程序中的调试信息编译要调试的程序。这样,gdb
才能够调试所 <BR>使用的变量、代码行和函数。如果要进行编译,请在 gcc(或
g++)下使用额外的 -g <BR>选项来编译程序: <BR>gcc -g eg.c -o eg
<BR>运行 gdb <BR>在 shell 中,可以使用 gdb 命令并指定程序名作为参数来运行
gdb,例如 gdb eg;或 <BR>者在 gdb 中,可以使用 file
命令来装入要调试的程序,例如 file eg。这两种方式都
<BR>假设您是在包含程序的目录中执行命令。装入程序之后,可以用 gdb 命令 un 来启动程
<BR>序。 <BR>调试会话示例 <BR>如果一切正常,程序将执行到结束,此时 gdb
将重新获得控制。但如果有错误将会怎么 <BR>样?这种情况下,gdb
会获得控制并中断程序,从而可以让您检查所有事物的状态,如
<BR>果运气好的话,可以找出原因。为了引发这种情况,我们将使用一个示例程序: <BR>代码示例
eg1.c <BR>#include <BR>int wib(int no1, int no2)
<BR>{ <BR>int result, diff; <BR>diff = no1 - no2;
<BR>result = no1 / diff; <BR>return result; <BR>}
<BR>int main(int argc, char *argv[]) <BR>{ <BR>int
value, div, result, i, total; <BR>value = 10;
<BR>div = 6; <BR>total = 0; <BR>for(i = 0; i <
10; i++) <BR>{ <BR>result = wib(value, div);
<BR>total += result; <BR>div++; <BR>value--; <BR>}
<BR>printf("%d wibed by %d equals %dn", value,
div, total); <BR>return 0; <BR>} <BR>这个程序将运行 10 次
for 循环,使用 wib() 函数计算出累积值,最后打印出结果。
<BR><BR>在您喜欢的文本编辑器中输入这个程序(要保持相同的行距),保存为 eg1.c,使用 g
<BR>cc -g eg1.c -o eg1 进行编译,并用 gdb eg1 启动 gdb。使用
un 运行程序可能会产 <BR>生以下消息: <BR>Program received signal
SIGFPE, Arithmetic exception. <BR>0x80483ea in wib
(no1=8, no2=8) at eg1.c:7 <BR>7 result = no1 /
diff; <BR>(gdb) <BR>gdb 指出在程序第 7
行发生一个算术异常,通常它会打印这一行以及 wib() 函数的自 <BR>变量值。要查看第 7
行前后的源代码,请使用 list 命令,它通常会打印 10 行。再次 <BR>输入
list(或者按回车重复上一条命令)将列出程序的下 10 行。从 gdb 消息中可以
<BR>看出,第 7 行中的除法运算出了错,程序在这一行中将变量 "no1" 除以 "diff"。
<BR>要查看变量的值,使用 gdb print 命令并指定变量名。输入 print no1 和
print dif <BR>f,可以相应看到 "no1" 和 "diff" 的值,结果如下:
<BR>(gdb) print no1 <BR>$5 = 8 <BR>(gdb) print
diff <BR>$2 = 0 <BR>gdb 指出 "no1" 等于 8,"diff" 等于
0。根据这些值和第 7 行中的语句,我们可以推 <BR>断出算术异常是由除数为 0
的除法运算造成的。清单显示了第 6 行计算的变量 "diff <BR>",我们可以打印 "diff"
表达式(使用 print no1 - no2 命令),来重新估计这个变 <BR>量。gdb 告诉我们
wib 函数的这两个自变量都等于 8,于是我们要检查调用 wib() 函 <BR>数的 main()
函数,以查看这是在什么时候发生的。在允许程序自然终止的同时,我们 <BR>使用 continue
命令告诉 gdb 继续执行。 <BR>(gdb) continue <BR>Continuing.
<BR>Program terminated with signal SIGFPE,
Arithmetic exception. <BR>The program no longer
exists. <BR>使用断点 <BR>为了查看在 main()
中发生了什么情况,可以在程序代码中的某一特定行或函数中设置 <BR>断点,这样 gdb
会在遇到断点时中断执行。可以使用命令reak main 在进入 main()
<BR>函数时设置断点,或者可以指定其它任何感兴趣的函数名来设置断点。然而,我们只希
<BR>望在调用 wib() 函数之前中断执行。输入 list main 将打印从 main()
函数开始的源 <BR>码清单,再次按回车将显示第 21 行上的 wib()
函数调用。要在那一行上设置断点,只 <BR>需输入reak 21。gdb 将发出以下响应:
<BR>(gdb) break 21 <BR>Breakpoint 1 at 0x8048428:
file eg1.c, line 21. <BR>以显示它已在我们请求的行上设置了 1 号断点。
un 命令将从头重新运行程序,直到 <BR>gdb 中断为止。发生这种情况时,gdb
会生成一条消息,指出它在哪个断点上中断,以 <BR>及程序运行到何处: <BR>Breakpoint
1, main (argc=1, argv=0xbffff954) at eg1.c:21
<BR>21 result = wib(value, div); <BR>发出 print
value 和 print div 将会显示在第一次调用 wib() 时,变量分别等于 10
<BR>和 6,而 print i 将会显示 0。幸好,gdb 将显示所有局部变量的值,并使用
info <BR>locals 命令保存大量输入信息。 <BR>从以上的调查中可以看出,当
"value" 和 "div" 相等时就会出现问题,因此输入 con <BR>tinue
继续执行,直到下一次遇到 1 号断点。对于这次迭代,info locals 显示了 va
<BR>lue=9 和 div=7。 <BR>与其再次继续,还不如使用 ext
命令单步调试程序,以查看 "value" 和 "div" 是如何 <BR>改变的。gdb 将响应:
<BR>(gdb) next <BR>22 total += result;
<BR>再按两次回车将显示加法和减法表达式: <BR>(gdb) <BR>23 div++;
<BR>(gdb) <BR>24 value--; <BR>再按两次回车将显示第 21
行,wib() 调用。info locals 将显示目前 "div" 等于 "v
<BR>alue",这就意味着将发生问题。如果有兴趣,可以使用 step 命令(与 ext 形成对比
<BR>, ext 将跳过函数调用)来继续执行 wib() 函数,以再次查看除法错误,然后使用 e
<BR>xt 来计算 "result"。 <BR>现在已完成了调试,可以使用 quit 命令退出
gdb。由于程序仍在运行,这个操作会终 <BR>止它,gdb 将提示您确认。
<BR>更多断点和观察点 <BR>由于我们想要知道在调用 wib() 函数之前 "value"
什么时候等于 "div",因此在上一 <BR>示例中我们在第 21
行中设置断点。我们必须继续执行两次程序才会发生这种情况,但
<BR>是只要在断点上设置一个条件就可以使 gdb 只在 "value" 与 "div"
真正相等时暂停。 <BR>要设置条件,可以在定义断点时指定 "break if "。将 eg1
再次装入 gdb,并输入: <BR>(gdb) break 21 if value==div
<BR>Breakpoint 1 at 0x8048428: file eg1.c, line
21. <BR>如果已经在第 21 行中设置了断点,如 1 号断点,则可以使用 condition
命令来代替 <BR>在断点上设置条件: <BR>(gdb) condition 1
value==div <BR>使用 un 运行 eg1.c 时,如果 "value" 等于
"div",gdb 将中断,从而避免了在它们 <BR>相等之前必须手工执行 continue。调试
C 程序时,断点条件可以是任何有效的 C 表达
<BR>式,一定要是程序所使用语言的任意有效表达式。条件中指定的变量必须在设置了断点
<BR>的行中,否则表达式就没有什么意义! <BR>使用 condition
命令时,如果指定断点编号但又不指定表达式,可以将断点设置成无条
<BR>件断点,例如,condition 1 就将 1 号断点设置成无条件断点。
<BR>要查看当前定义了什么断点及其条件,请发出命令 info break: <BR>(gdb)
info break <BR>Num Type Disp Enb Address What
<BR>1 breakpoint keep y 0x08048428 in main at
eg1.c:21 <BR>stop only if value == div
<BR>breakpoint already hit 1 time
<BR>除了所有条件和已经遇到断点多少次之外,断点信息还在 Enb 列中指定了是否启用该断
<BR>点。可以使用命令 disable 、enable 或 delete
来禁用、启用和彻底删除断点,例如 <BR>disable 1 将阻止在 1 号断点处中断。
<BR>如果我们对 "value" 什么时候变得与 "div" 相等更感兴趣,那么可以使用另一种断点
<BR>,称作监视。当指定表达式的值改变时,监视点将中断程序执行,但必须在表达式中所
<BR>使用的变量在作用域中时设置监视点。要获取作用域中的 "value" 和 "div",可以在
<BR>main 函数上设置断点,然后运行程序,当遇到 main() 断点时设置监视点。重新启动
<BR>gdb,并装入 eg1,然后输入: <BR>(gdb) break main
<BR>Breakpoint 1 at 0x8048402: file eg1.c, line
15. <BR>(gdb) run <BR>... <BR>Breakpoint 1, main
(argc=1, argv=0xbffff954) at eg1.c:15 <BR>15 value
= 10; <BR>要了解 "div" 何时更改,可以使用 watch div,但由于要在
"div" 等于 "value" 时中 <BR>断,那么应输入: <BR>(gdb) watch
div==value <BR>Hardware watchpoint 2: div == value
<BR>如果继续执行,那么当表达式 "div==value" 的值从 0(假)变成
1(真)时,gdb 将 <BR>中断: <BR>(gdb) continue
<BR>Continuing. <BR>Hardware watchpoint 2: div ==
value <BR>Old value = 0 <BR>New value = 1 <BR>main
(argc=1, argv=0xbffff954) at eg1.c:19 <BR>19 for(i
= 0; i < 10; i++) <BR>info locals 命令将验证 "value"
是否确实等于 "div"(再次声明,是 8)。 <BR>info watch
命令将列出已定义的监视点和断点(此命令等价于 info break),而且可
<BR>以使用与断点相同的语法来启用、禁用和删除监视点。 <BR>core 文件 <BR>在 gdb
下运行程序可以使俘获错误变得更容易,但在调试器外运行的程序通常会中止而 <BR>只留下一个 core
文件。gdb 可以装入 core 文件,并让您检查程序中止之前的状态。 <BR><BR>在 gdb
外运行示例程序 eg1 将会导致核心信息转储: <BR>$ ./eg1 <BR>Floating
point exception (core dumped) <BR>要使用 core 文件启动
gdb,在 shell 中发出命令 gdb eg1 core 或 gdb eg1 -c cor
<BR>e。gdb 将装入 core 文件,eg1 的程序清单,显示程序是如何终止的,并显示非常类似
<BR>于我们刚才在 gdb 下运行程序时看到的消息: <BR>... <BR>Core was
generated by `./eg1. <BR>Program terminated with
signal 8, Floating point exception. <BR>... <BR>#0
0x80483ea in wib (no1=8, no2=8) at eg1.c:7 <BR>7
result = no1 / diff; <BR>此时,可以发出 info
locals、print、info args 和 list 命令来查看引起除数为零的
<BR>值。info variables 命令将打印出所有程序变量的值,但这要进行很长时间,因为
gd <BR>b 将打印 C 库和程序代码中的变量。为了更容易地查明在调用 wib()
的函数中发生了 <BR>什么情况,可以使用 gdb 的堆栈命令。 <BR>堆栈跟踪
<BR>程序“调用堆栈”是当前函数之前的所有已调用函数的列表(包括当前函数)。每个函
<BR>数及其变量都被分配了一个“帧”,最近调用的函数在 0 号帧中(“底部”帧)。要打
<BR>印堆栈,发出命令tacktrace [回溯] 的缩写): <BR>(gdb) bt
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -