Linux下C++的编程——GDB进行程序调试

注册 Vultr VPS 送你10美金 免费玩4个月

GDB简介

我们在Linux下C++的编程——开偏介绍一文中已经简单介绍了GDB的功能,是类Unix系统的主要调试工具,可进行断点调试,跟踪程序,动态改变执行环境等功能。

从一个程序开始调试

下面我们就从一个程序开始讲解一下GDB的简单用法。假设我们有如下的程序:
GDBTest1.cpp

#include <iostream>

int Accumulation(int n)
{
        int result = 0;
        for(int i = 0; i < n; i ++) 
        {
                result += i;
        }
        return result;
}

int main()
{
        int value = 0;
        int v1 = 0, v2 = 0;
        std::cout << "Please inupt two number" << std::endl;
        std::cin >> v1 >> v2; 
        std::cout << "the sum of " << v1 << " and " << v2 << "is" << value << std::endl;
        std::cout << "the sum bettween 1 to 5 is " << Accumulation(5) << std::endl;
        return 0;
}

对它进行编译并执行:
[l[email protected] GDBTest1]vimGDBTest1.cpp[luowf@luoweifuGDBTest1] g++ -o GDBTest1 GDBTest1.cpp
[[email protected] GDBTest1]lsGDBTest1GDBTest1.cpp[luowf@luoweifuGDBTest1] ./GDBTest1
Please inupt two number
4 5
the sum of 4 and 5 is: 0
the sum bettween 1 to 5 is: 10
[[email protected] GDBTest1]$

可见这并不是我们想要的结果。我们这个程序中Accumulation(int n)的功能是想求1到n之间所有数的和,而main函数是要求输入两个数并求这两个数的和,上面的例子中就也就是希望得到4+5 = 9,1到5所有整数的和为15。

接下来我们就要对这个程序进行调试,怎么调试呢?就要用到GDB。

[[email protected] GDBTest1]$ gdb GDBTest1
GNU gdb (GDB) Red Hat Enterprise Linux (7.2-83.el6)
...
Reading symbols from /home/luowf/workspace/Cplusplus/GDBTest1/GDBTest1...(no debugging symbols found)...done.
(gdb) l----------------------------->查看源码
No symbol table is loaded.  Use the "file" command.
(gdb) 

正常情况下,我们在上面输入l(list)是希望查看源代码的,但上面并没有显示源码。这是因为我们编译时没有保存调试信息。我们可以通过参数-g把调试信息加到可执行文件中。

Ok,我们重新编译并调试。如下:

[[email protected] GDBTest1]$ g++ -g -o GDBTest1 GDBTest1.cpp 
[[email protected] GDBTest1]$ gdb GDBTest1

Reading symbols from /home/luowf/workspace/Cplusplus/GDBTest1/GDBTest1...done.
(gdb) l--------------------------------------->查看源代码
6       for(int i = 0; i < n; i ++)
7       {
8           result += i;
9       }
10      return result;
11  }
12  
13  int main()
14  {
15      int value = 0;
(gdb) l 0------------------------------------>查看第0行的源代码
1   #include <iostream>
2   
3   int Accumulation(int n)
4   {
5       int result = 0;
6       for(int i = 0; i < n; i ++)
7       {
8           result += i;
9       }
10      return result;
(gdb) b 8------------------------------------->在第8行加判断
Breakpoint 1 at 0x804871a: file GDBTest1.cpp, line 8.
(gdb) l--------------------------------------->查看源代码
11  }
12  
13  int main()
14  {
15      int value = 0;
16      int v1 = 0, v2 = 0;
17      std::cout << "Please inupt two number" << std::endl;
18      std::cin >> v1 >> v2;
19      std::cout << "the sum of " << v1 << " and " << v2 << " is: " << value << std::endl;
20      std::cout << "the sum bettween 1 to 5 is: " << Accumulation(5) << std::endl;
(gdb) b 10------------------------------------->在第10行加判断
Breakpoint 2 at 0x8048731: file GDBTest1.cpp, line 10.
(gdb) r---------------------------------------->开始执行程序
Starting program: /home/luowf/workspace/Cplusplus/GDBTest1/GDBTest1 
Please inupt two number
4 5
the sum of 4 and 5 is: 0

Breakpoint 1, Accumulation (n=5) at GDBTest1.cpp:8
8           result += i;
Missing separate debuginfos, use: debuginfo-install glibc-2.12-1.166.el6_7.1.i686 libgcc-4.4.7-16.el6.i686 libstdc++-4.4.7-16.el6.i686
(gdb) p result--------------------------------->打印result变量的值(print)
$1 = 0
(gdb) c---------------------------------------->执行到下一次断点(continue)
Continuing.

Breakpoint 1, Accumulation (n=5) at GDBTest1.cpp:8
8           result += i;
(gdb) c---------------------------------------->执行到下一次断点(continue)
Continuing.

Breakpoint 1, Accumulation (n=5) at GDBTest1.cpp:8
8           result += i;
(gdb) p result
$2 = 1
(gdb) c
Continuing.

Breakpoint 1, Accumulation (n=5) at GDBTest1.cpp:8
8           result += i;
(gdb) c
Continuing.

Breakpoint 1, Accumulation (n=5) at GDBTest1.cpp:8
8           result += i;
(gdb) p result
$3 = 6
(gdb) p i-------------------------------------->打印i变量的值
$4 = 4
(gdb) n---------------------------------------->执行一行代码(next)
6       for(int i = 0; i < n; i ++)
(gdb) n---------------------------------------->执行一行代码(next)

Breakpoint 2, Accumulation (n=5) at GDBTest1.cpp:10
10      return result;

从上面我们可以看到当执行到i=4时不再执行循环内的代码,再查看一下我们的代码会发现是我们的循环条件写错,应该是i <= n。

OK,我们用vim修改程序

#include <iostream>

int Accumulation(int n)
{
        int result = 0;
        for(int i = 0; i <= n; i ++) 
        {
                result += i;
        }
        return result;
}

int main()
{
        int value = 0;
        int v1 = 0, v2 = 0;
        std::cout << "Please inupt two number" << std::endl;
        std::cin >> v1 >> v2; 
        value = v1 + v2; 
        std::cout << "the sum of " << v1 << " and " << v2 << " is: " << value << std::endl;
        std::cout << "the sum bettween 1 to 5 is: " << Accumulation(5) << std::endl;
        return 0;
}

再次执行

[[email protected] GDBTest1]$ g++ -g -o GDBTest1 GDBTest1.cpp 
[[email protected] GDBTest1]$ ls
GDBTest1  GDBTest1.cpp
[[email protected] GDBTest1]$ ./GDBTest1 
Please inupt two number
4 5
the sum of 4 and 5 is: 9
the sum bettween 1 to 5 is: 15

Ok,这已经是我们需要的结果了。

GDB的常用命令

命令 解释 示例
file <文件名> 加载被调试的可执行程序文件。 因为一般都在被调试程序所在目录下执行GDB,因而文本名不需要带路径。 (gdb) file gdb-sample
r Run的简写,运行被调试的程序。 如果此前没有下过断点,则执行完整个程序;如果有断点,则程序暂停在第一个可用断点处。 (gdb) r
c Continue的简写,继续执行被调试程序,直至下一个断点或程序结束。 (gdb) c
b <行号> b <函数名称> b <函数名称> b <代码地址> d [编号] b: Breakpoint的简写,设置断点。两可以使用“行号”“函数名称”“执行地址”等方式指定断点位置。 其中在函数名称前面加“*”符号表示将断点设置在“由编译器生成的prolog代码处”。如果不了解汇编,可以不予理会此用法。 d: Delete breakpoint的简写,删除指定编号的某个断点,或删除所有断点。断点编号从1开始递增。 (gdb) b 8 (gdb) b main (gdb) b *main (gdb) b *0x804835c (gdb) d
s, n s: 执行一行源程序代码,如果此行代码中有函数调用,则进入该函数; n: 执行一行源程序代码,此行代码中的函数调用也一并执行。 s 相当于其它调试器中的“Step Into (单步跟踪进入)”; n 相当于其它调试器中的“Step Over (单步跟踪)”。 这两个命令必须在有源代码调试信息的情况下才可以使用(GCC编译时使用“-g”参数)。 (gdb) s (gdb) n
si, ni si命令类似于s命令,ni命令类似于n命令。所不同的是,这两个命令(si/ni)所针对的是汇编指令,而s/n针对的是源代码。 (gdb) si (gdb) ni
p <变量名称> Print的简写,显示指定变量(临时变量或全局变量)的值。 (gdb) p i (gdb) p nGlobalVar
display … undisplay <编号> display,设置程序中断后欲显示的数据及其格式。 例如,如果希望每次程序中断后可以看到即将被执行的下一条汇编指令,可以使用命令 “display /i pcpc 代表当前汇编指令,/i 表示以十六进行显示。当需要关心汇编代码时,此命令相当有用。 undispaly,取消先前的display设置,编号从1开始递增。 (gdb) display /i $pc (gdb) undisplay 1
i info的简写,用于显示各类信息,详情请查阅“help i”。 (gdb) i r
q Quit的简写,退出GDB调试环境。 (gdb) q
help [命令名称] GDB帮助命令,提供对GDB名种命令的解释说明。 如果指定了“命令名称”参数,则显示该命令的详细说明;如果没有指定参数,则分类显示所有GDB命令,供用户进一步浏览和查询。 (gdb) help

以上表格来自: http://man.linuxde.net/gdb



上一篇文章:
Linux下C++的编程——开发环境搭建与第一个程序

下一篇要讲述的内容:
Linux下C++的编程——Makefile的简单用法

注册 Vultr VPS 送你10美金 免费玩4个月