掌握运用 GDB 调试代码

有错误的代码

要开始使用 GDB,你需要一些代码。这里有一个用 C++ 写的示例应用程序(如果你一般不使用 C++ 编写程序也没关系,在所有语言中原理都是一样的)


  1. #include <iostream>
  2. #include <stdlib.h> //srand
  3. #include <stdio.h> //printf
  4.  
  5. using namespace std;
  6.  
  7. int main () {
  8.  
  9. srand (time(NULL));
  10. int alpha = rand() % 8;
  11. cout << "Hello world." << endl;
  12. int beta = 2;
  13.  
  14. printf("alpha is set to is %s ", alpha);
  15. printf("kiwi is set to is %s ", beta);
  16.  
  17. return 0;
  18. } // main

这个代码示例中有一个 bug,但它确实可以编译(至少在 GCC 5 的时候)。如果你熟悉 C++,你可能已经看到了,但这是一个简单的问题,可以帮助新的 GDB 用户了解调试过程。编译并运行它就可以看到错误:


  1. $ g++ -o buggy example.cpp
  2. $ ./buggy
  3. Hello world.
  4. Segmentation fault

排除段故障

从这个输出中,你可以推测变量 alpha 的设置是正确的,因为否则的话,你就不会看到它后面的那行代码执行。当然,这并不总是正确的,但这是一个很好的工作理论,如果你使用 printf 作为日志和调试器,基本上也会得出同样的结论。从这里,你可以假设 bug 在于成功打印的那一行之后的某行。然而,不清楚错误是在下一行还是在几行之后。

GNU 调试器是一个交互式的故障排除工具,所以你可以使用 gdb 命令来运行错误的代码。为了得到更好的结果,你应该从包含有调试符号的源代码中重新编译你的错误应用程序。首先,看看 GDB 在不重新编译的情况下能提供哪些信息:


  1. $ gdb ./buggy
  2. Reading symbols from ./buggy...done.
  3. (gdb) start
  4. Temporary breakpoint 1 at 0x400a44
  5. Starting program: /home/seth/demo/buggy
  6.  
  7. Temporary breakpoint 1, 0x0000000000400a44 in main ()
  8. (gdb)

当你以一个二进制可执行文件作为参数启动 GDB 时,GDB 会加载该应用程序,然后等待你的指令。因为这是你第一次在这个可执行文件上运行 GDB,所以尝试重复这个错误是有意义的,希望 GDB 能够提供进一步的见解。很直观,GDB 用来启动它所加载的应用程序的命令就是 start。默认情况下,GDB 内置了一个断点,所以当它遇到你的应用程序的 main 函数时,它会暂停执行。要让 GDB 继续执行,使用命令 continue


  1. (gdb) continue
  2. Continuing.
  3. Hello world.
  4.  
  5. Program received signal SIGSEGV, Segmentation fault.
  6. 0x00007ffff71c0c0b in vfprintf () from /lib64/libc.so.6
  7. (gdb)

毫不意外:应用程序在打印 “Hello world” 后不久就崩溃了,但 GDB 可以提供崩溃发生时正在发生的函数调用。这有可能就足够你找到导致崩溃的 bug,但为了更好地了解 GDB 的功能和一般的调试过程,想象一下,如果问题还没有变得清晰,你想更深入地挖掘这段代码发生了什么。

【声明】:芜湖站长网内容转载自互联网,其相关言论仅代表作者个人观点绝非权威,不代表本站立场。如您发现内容存在版权问题,请提交相关链接至邮箱:bqsm@foxmail.com,我们将及时予以处理。

相关文章