gcc -g eg.c -o eg

运行 gdb

#include
int wib(int no1, int no2)
{
int result, diff;
diff = no1 - no2;
result = no1 / diff;
return result;
}
int main(int argc, char *argv[])
{
int value, div, result, i, total;
value = 10;
div = 6;
total = 0;
for(i = 0; i < 10; i++)
{
result = wib(value, div);
total += result;
div++;
value--;
}
printf("%d wibed by %d equals %d\n", value, div, total);
return 0;
}



Program received signal SIGFPE, Arithmetic exception.
0x80483ea in wib (no1=8, no2=8) at eg1.c:7
7         result = no1 / diff;
(gdb)


gdb 指出在程序第 7 行发生一个算术异常，通常它会打印这一行以及 wib() 函数的自变量值。要查看第 7 行前后的源代码，请使用 'list' 命令，它通常会打印 10 行。再次输入 'list'（或者按回车重复上一条命令）将列出程序的下 10 行。从 gdb 消息中可以看出，第 7 行中的除法运算出了错，程序在这一行中将变量 "no1" 除以 "diff"。


(gdb) print no1
$5 = 8 (gdb) print diff$2 = 0


gdb 指出 "no1" 等于 8，"diff" 等于 0。根据这些值和第 7 行中的语句，我们可以推断出算术异常是由除数为 0 的除法运算造成的。清单显示了第 6 行计算的变量 "diff"，我们可以打印 "diff" 表达式（使用 'print no1 - no2' 命令），来重新估计这个变量。gdb 告诉我们 wib 函数的这两个自变量都等于 8，于是我们要检查调用 wib() 函数的 main() 函数，以查看这是在什么时候发生的。在允许程序自然终止的同时，我们使用 'continue' 命令告诉 gdb 继续执行。


(gdb) continue
Continuing.
Program terminated with signal SIGFPE, Arithmetic exception.
The program no longer exists.



(gdb) break 21
Breakpoint 1 at 0x8048428: file eg1.c, line 21.



Breakpoint 1, main (argc=1, argv=0xbffff954) at eg1.c:21
21          result = wib(value, div);



(gdb) next
22          total += result;



(gdb)
23          div++;
(gdb)
24          value--;



(gdb) break 21 if value==div
Breakpoint 1 at 0x8048428: file eg1.c, line 21.



(gdb) condition 1 value==div



(gdb) info break
Num Type           Disp Enb Address    What
1   breakpoint     keep y   0x08048428 in main at eg1.c:21
stop only if value == div



(gdb) break main
Breakpoint 1 at 0x8048402: file eg1.c, line 15.
(gdb) run
...
Breakpoint 1, main (argc=1, argv=0xbffff954) at eg1.c:15
15        value = 10;



(gdb) watch div==value
Hardware watchpoint 2: div == value



(gdb) continue
Continuing.
Hardware watchpoint 2: div == value
Old value = 0
New value = 1
main (argc=1, argv=0xbffff954) at eg1.c:19
19        for(i = 0; i < 10; i++)


'info locals' 命令将验证 "value" 是否确实等于 "div"（再次声明，是 8）。

'info watch' 命令将列出已定义的监视点和断点（此命令等价于 'info break'），而且可以使用与断点相同的语法来启用、禁用和删除监视点。


$./eg1 Floating point exception (core dumped)   要使用 core 文件启动 gdb，在 shell 中发出命令 'gdb eg1 core' 或 'gdb eg1 -c core'。gdb 将装入 core 文件，eg1 的程序清单，显示程序是如何终止的，并显示非常类似于我们刚才在 gdb 下运行程序时看到的消息：   ... Core was generated by ./eg1'. Program terminated with signal 8, Floating point exception. ... #0 0x80483ea in wib (no1=8, no2=8) at eg1.c:7 7 result = no1 / diff;  此时，可以发出 'info locals'、'print'、'info args' 和 'list' 命令来查看引起除数为零的值。'info variables' 命令将打印出所有程序变量的值，但这要进行很长时间，因为 gdb 将打印 C 库和程序代码中的变量。为了更容易地查明在调用 wib() 的函数中发生了什么情况，可以使用 gdb 的堆栈命令。 程序“调用堆栈”是当前函数之前的所有已调用函数的列表（包括当前函数）。每个函数及其变量都被分配了一个“帧”，最近调用的函数在 0 号帧中（“底部”帧）。要打印堆栈，发出命令 'bt'（'backtrace' [回溯] 的缩写）：   (gdb) bt #0 0x80483ea in wib (no1=8, no2=8) at eg1.c:7 #1 0x8048435 in main (argc=1, argv=0xbffff9c4) at eg1.c:21  此结果显示了在 main() 的第 21 行中调用了函数 wib()（只要使用 'list 21' 就能证实这一点），而且 wib() 在 0 号帧中，main() 在 1 号帧中。由于 wib() 在 0 号帧中，那么它就是执行程序时发生算术错误的函数。 实际上，发出 'info locals' 命令时，gdb 会打印出当前帧中的局部变量，缺省情况下，这个帧中的函数就是被中断的函数（0 号帧）。可以使用命令 'frame' 打印当前帧。要查看 main 函数（在 1 号帧中）中的变量，可以发出 'frame 1' 切换到 1 号帧，然后发出 'info locals' 命令：   (gdb) frame 1 #1 0x8048435 in main (argc=1, argv=0xbffff9c4) at eg1.c:21 21 result = wib(value, div); (gdb) info locals value = 8 div = 8 result = 4 i = 2 total = 6  此信息显示了在第三次执行 "for" 循环时（i 等于 2）发生了错误，此时 "value" 等于 "div"。 可以通过如上所示在 'frame' 命令中明确指定号码，或者使用 'up' 命令在堆栈中上移以及 'down' 命令在堆栈中下移来切换帧。要获取有关帧的进一步信息，如它的地址和程序语言，可以使用命令 'info frame'。 gdb 堆栈命令可以在程序执行期间使用，也可以在 core 文件中使用，因此对于复杂的程序，可以在程序运行时跟踪它是如何转到函数的。 除了调试 core 文件或程序之外，gdb 还可以连接到已经运行的进程（它的程序已经过编译，并加入了调试信息），并中断该进程。只需用希望 gdb 连接的进程标识替换 core 文件名就可以执行此操作。以下是一个执行循环并睡眠的 示例程序 eg2 示例代码 #include int main(int argc, char *argv[]) { int i; for(i = 0; i < 60; i++) { sleep(1); } return 0; }  使用 'gcc -g eg2.c -o eg2' 编译该程序并使用 './eg2 &' 运行该程序。请留意在启动该程序时在背景上打印的进程标识，在本例中是 1283：   ./eg2 & [3] 1283  启动 gdb 并指定进程标识，在我举的这个例子中是 'gdb eg2 1283'。gdb 会查找一个叫作 "1283" 的 core 文件。如果没有找到，那么只要进程 1283 正在运行（在本例中可能在 sleep() 中），gdb 就会连接并中断该进程：   ... /home/seager/gdb/1283: No such file or directory. Attaching to program: /home/seager/gdb/eg2, Pid 1283 ... 0x400a87f1 in __libc_nanosleep () from /lib/libc.so.6 (gdb)  此时，可以发出所有常用 gdb 命令。可以使用 'backtrace' 来查看当前位置与 main() 的相对关系，以及 mian() 的帧号是什么，然后切换到 main() 所在的帧，查看已经在 "for" 循环中运行了多少次：   (gdb) backtrace #0 0x400a87f1 in __libc_nanosleep () from /lib/libc.so.6 #1 0x400a877d in __sleep (seconds=1) at ../sysdeps/unix/sysv/linux/sleep.c:78 #2 0x80483ef in main (argc=1, argv=0xbffff9c4) at eg2.c:7 (gdb) frame 2 #2 0x80483ef in main (argc=1, argv=0xbffff9c4) at eg2.c:7 7 sleep(1); (gdb) print i$1 = 50


gdb 可以让您通过使用 shell 命令在不退出调试环境的情况下运行 shell 命令，调用形式是 'shell [commandline]'，这有助于在调试时更改源代码。

 
Breakpoint 1, wib (no1=8, no2=8) at eg1.c:7
7         result = no1 / diff;
(gdb) print diff
\$1 = 0
(gdb) set diff=1
(gdb) continue
Continuing.
0 wibed by 16 equals 10
Program exited normally.
`

GNU 调试器是所有程序员工具库中的一个功能非常强大的工具。在本文中，我只介绍了 gdb 的一小部分功能。要了解更多知识，建议您阅读 GNU 调试器手册。

posted on 2011-03-30 17:59  一顽石一  阅读(409)  评论(0编辑  收藏  举报