您的位置:首页 > 运维架构 > Linux

Unix/Linux C++应用开发-gdb工具调试命令

2011-11-18 11:31 375 查看
 

一、  gdb装载调试程序

前面一篇,完整实例调试小节,已经简单介绍在gdb工具中如何加载需要调试的可执行应用程序,从而来实现相应的调试应用。

根据前面介绍的gdb启动程序的方法,可以在当前程序目录直接执行gdb程序,启动信息如下。

[developer@localhost developer]$ gdb                                                   //在shell下执行gdb启动程序命令

GNU gdb Red Hat Linux (5.3post-0.20021129.18rh)                                     //以下为gdb工具启动信息

Copyright 2003 Free Software Foundation, Inc.

GDB is free software, covered by the GNU General Public License, and you are

welcome to change it and/or distribute copies of it under certain conditions.

Type "show copying" to see the conditions.

There is absolutely no warranty for GDB.  Type "show warranty" for details.

This GDB was configured as "i386-redhat-linux-gnu".

(gdb)                                                                                                                //gdb会话位置,供调试者执行命令

当开发者在当前shell下直接执行gdb命令来启动该工具后,想要再装载需要调试的可执行程序,此时就需要该工具提供的file命令来指定目标可执行程序进行加载。

(gdb) file chapter0301                                                     //装载调试程序

Reading symbols from chapter0301...done.

当前gdb启动的情况下,单独使用file命令其实就类似于图形界面中提供的打开文件的功能。上述简单的file应用命令操作,将可执行程序chapter0301加载至gdb调试工具中,此命令使用与gdb chapter0301功能相同,随后可以在该会话中使用其它命令实现调试功能。该调试工具加载可执行程序chapter0301之后,当前调试会话中调试该程序,如果需要当前目录中调试其它可执行程序,可以通过file命令直接在该会话中重新加载需要调试的程序名,会切换至新的调试会话中,之前的调试会话被覆盖。

二、  gdb运行调试程序

当装载进入可执行程序之后,需要在该调试工具中运行该程序,查看程序运行的情况。通常需要使用gdb调试工具来调试的程序往往都存在一定的隐性处理错误,所谓的隐性错误是指隐藏在程序逻辑内部,在程序编译过程中编译器无法找出的错误,正常情况下应用程序产生的大部分语法错误应该在程序编译过程中通过查错提示能够得到解决。

1.使用run加载程序

在当前的gdb调试工具中可以使用run命令或者简写r命令,执行加载需要调试的可执行程序,从而查看程序运行是否达到所期望的结果。一旦程序中设置断点,那么执行该命令运行程序会在第一个断点处停住,便于开发者进行下一步的操作。下面依然可以通过直接执行chapter0301程序实例来了解该命令的运作情况。

(gdb) run

Starting program: /mnt/hgfs/share/worktest/linux_c++/gdb/chapter0301

Get current time:

20081216141438

Program exited normally.

从上述演示过程可见,当当前调试工具中针对该程序没有设置任何的断点时,执行run命令会直接运行得出最终程序处理的结果。此处需要注意的是,该命令的执行只会看到打印输出部分的结果,至于中间运行的过程,则需要使用到单步调试来实现。

2.使用进程号加载

对于gdb调试工具,在当前会话中依然可以使用run命令直接加调试程序的进程号,在程序运行的情况下进行程序多进程调试。通常在Linux平台的shell下执行两个同一可执行程序会为其分配不同进程号,在gdb工具下可以根据查找到当前可执行程序运行状态下的进程号,在当前会话中执行调试程序。此处依然采用chapter0301的实例程序来作演示,该程序作出修改如下。

//main函数最后添加语句

for(;;)

  ;

增加上述空循环的目的是为了在当前循环执行进程期间,根据系统分配的进程号来调试该程序。上述程序编译执行之后,可以通过当前shell下执行ps命令来获取其进程号,该命令执行结果如下所示。

[developer@localhost]$ ps -ef|grep chapter0301                                           //ps命令查看进程信息

500       2279  2223 94 08:42 pts/0    00:00:16 ./chapter0301

500       2313  2280  0 08:42 pts/1    00:00:00 grep chapter0301

对于Linux下shell提供的ps命令,初学者此时只需要了解其基本的查看进程状态的功能即可。上述ps命令首先使用了-e与-f选项结合功能,ps命令的-e表示显示系统所有正在运行的进程信息,-f则表示以长列表的形式显示每个进程的信息。

组合之后的ps功能即显示系统中所有正在运行的进程信息,以长列表形式在屏幕输出,另外配合grep查找命令,查找所有进程中为chapter0301的进程信息。所以最终打印输出的仅仅为指定进程名的对应信息。从上述演示结果看来,chapter0301进程号为2223,因此在当前的gdb调试工具中操作如下。

(gdb) file chapter0301

Reading symbols from chapter0301...done.

(gdb) run 2223

Starting program: /mnt/hgfs/share/worktest/linux_c++/gdb/chapter0301 2223

Get current time:

20081221123517

由于大部分生产型的系统都是一天24小时不间断运行,因此可以通过gdb工具中直接运行相应的进程号就可以实现不影响现有应用程序下的调试工作。上述实例首先在gdb工具下通过file命令加载可执行程序,随后根据查找获取的在线应用进程号,在该工具下通过run等命令来执行调试功能。

三、  gdb设置断点

gdb工具加载可执行程序文件之后,通过上小节介绍的run命令可以执行该程序,直到出错处或者程序运行的结果处。该命令执行过程会走完整个可执行程序处理流程,当开发者通过初步考虑程序出错信息断定出错的位置时,可以使用gdb工具提供的break命令来设置相应的断点。

对于计算机应用程序来讲,断点即为程序被中断的地方。具体理解为程序在执行规定处理流程中被打断暂停当前任务从而转向去执行新的任务的过程。随后当新任务执行完成之后可以回到当前断点处继续处理完程序指定的任务。

总之通过在程序中设定相应的断点,可以保证在当前调试工具中执行该程序后到指定位置停止,供开发者查阅此时程序执行的相关情况,来达到排查可能发生的错误。gdb调试工具中提供了break命令在应用程序中设置断点,该命令也可以简写为b。

1.break基本使用方法

调试工具break命令使用的具有几种不同的操作格式,shell下该命令使用的基本语法规则如下所示。

(gdb)break [function][row number]

当需要调试的可执行程序在gdb中已经加载之后,设置程序断点可以直接采用break命令后加相应的函数名称,或者代码的行数。设定好断点之后,通常gdb工具会打印相关断点设置的基本信息。实例chapter0301考虑到程序执行的主要流程,在主函数以及获取当前时间方法处设置断点如下所示。

(gdb) break main

Breakpoint 1 at 0x80489f5: file chapter0301.cpp, line 23.

(gdb) break 25

Breakpoint 2 at 0x8048a2a: file chapter0301.cpp, line 25.

(gdb) break date_string

Breakpoint 3 at 0x8048923: file chapter0301.cpp, line 7.

(gdb) break 11

Breakpoint 4 at 0x8048945: file chapter0301.cpp, line 11.

上述演示表明,在当前调试的应用文件中根据需要设置4个断点位置。断点break命令后直接加函数名设置了main、date_string函数处断点,根据需要指定了程序文件中代码位置处行号来设定另外两个断点位置。断点设置随后会打印输出该断点的相关基本信息,断点的序列号、断点的地址以及断点在代码文件中的行号位置等。

开发者可以根据设置的断点,在当前调试会话中执行run命令,程序运行后会在其第一个断点位置处中断,供调试者查看相关变量、单步调试等操作来诊断程序。

2.多文件断点设置

另外对于更高的调试需求,gdb也提供了更多断点设置的功能。比如在多文件的可执行程序中,可以通过指定文件相关的断点编号或者方法名等来设置跨文件断点。也提供了程序调试中在特定条件许可情况下触发断点执行的功能。该类断点特殊应用方式调试实例如下所示。

//实例chapter0302

//chapter0302_01.h

#ifndef     CHAPTER0302_01_H

#define    CHAPTER0302_01_H

#include <iostream>

using namespace std;

/*获取当前时间方法声明*/

string date_string();

#endif

 

//chapter0302_01.cpp

#include "chapter0302_01.h"

/*获取当前时间方法定义*/

string date_string()

{

         time_t clock = time(NULL);                                                      //求取当前日历时间

         struct tm* stm = localtime(&clock);                                         //根据当前日历时间求取相应本地时间

         char buffer[15];                                                                                    //定义字符数组存放时间字符串的变量

 

         sprintf(buffer, "%4d%02d%02d%02d%02d%02d",            //通过sprintf方法格式化时间字符串

                   stm->tm_year + 1900,

                   stm->tm_mon + 1,

                   stm->tm_mday,

                   stm->tm_hour,

                   stm->tm_min,

                   stm->tm_sec);

         return buffer;                                                                               //返回时间字符串求取的结果

}

 

//chapter0302_02.cpp

#include "chapter0302_01.h"

/*主函数入口*/

int main()

{

         string date ;                                                                                  //定义时间字符串变量

    cout<<"Get current time:"<<endl;                                            //打印输出时间提示信息

    date = date_string();                                                                  //调用时间获取方法求取当前时间

    cout<<date<<endl;                                                                     //打印获取的当前时间字符串结果

         return 0;

}

为了演示多文件断点设置功能,实例3-1代码被修改为多文件方式实现同样的功能。程序同样采用加-g参数,多文件C++程序编译命令编辑如下所示。

g++ -g chapter0302_01.cpp chapter0302_02.cpp –o chapter0302_02

当前shell下执行该编译命令后,生成相应可执行程序。该程序实现的功能与chapter0301实例相同,可执行程序生成之后,通过gdb命令加载该程序。在当前调试会话中,想要在不同的文件中设置断点,以及在特定条件下执行生效的断点演示如下所示。

(gdb) break chapter0302_01.cpp:date_string                               //通过break命令在当前指定的cpp文件设置断点

Breakpoint 1 at 0x80488eb: file chapter0302_01.cpp, line 5.    //断点设置信息提示

(gdb) info break                                                                                   //通过info查看当前调试程序所有断点信息

Num Type           Disp Enb Address    What

1   breakpoint     keep y   0x080488eb in date_string() at chapter0302_01.cpp:5

(gdb) break chapter0302_01.cpp:16 if buffer != 0                        //通过break命令根据程序判断条件设定断点

Breakpoint 2 at 0x8048949: file chapter0302_01.cpp, line 16.

(gdb) info break                                                                                   //查看当前调试程序断点信息

Num Type           Disp Enb Address    What

1   breakpoint     keep y   0x080488eb in date_string() at chapter0302_01.cpp:5

2   breakpoint     keep y   0x08048949 in date_string() at chapter0302_01.cpp:16

        stop only if buffer != 0

(gdb) r                                                                                                    //当前调试会话中执行该调试程序

Starting program: /mnt/hgfs/share/worktest/linux_c++/gdb/chapter0302_02

Get current time:                                                                                  //运行至第一个断点设置处,程序停下

Breakpoint 1, date_string() () at chapter0302_01.cpp:5

5                 time_t clock = time(NULL);                                     //打印当前程序运行的代码行位置信息

(gdb) c                                                                                                   //通过continue命令使得程序从断点处继续执行

Continuing.

Breakpoint 2, date_string() () at chapter0302_01.cpp:16            //程序运行至下一个断点位置停下

16          return buffer;

上述过程主要演示了多文件中跨文件断点设置与特定情况下断点生效功能,为了提供更多的调试功能,gdb工具允许在设置断点时指定该程序相关的代码文件对应的行或者方法名,同时也允许调试者在假设的情况设置断点。

实例中加载调试程序后,通过break命令后加指定文件名,冒号之后为指定断点的位置或者名称。当创建跨文件断点之后,通过info命令查看创建断点的情况。另外一种是指定断点位置后,通过增加判断条件buffer是不是为空来指定断点在什么样的情况下生效。

随后通过run命令来验证,在当前会话中断点运行情况。由于程序运行到指定函数断点处停止,因此输出该断点运行处第一行代码信息,通过continue(简写c)命令来继续执行下一个断点,上述结果表明符合断点判断信息,因此会在断点位置中断程序运行。

四、  gdb断点管理

当开发者在调试应用程序中设置断点之后,gdb工具提供了一系列的简单命令用于管理这些断点。通过这些基本的断点管理命令,用户可以实现当前会话中的程序断点基本管理。首先,gdb中断点设置完毕之后可以通过提供的info命令显示当前调试会话中所有断点基本信息。

(gdb) info break

No breakpoints or watchpoints.

(gdb) info break

Num Type           Disp Enb Address    What

1   breakpoint     keep y   0x080489bd in main at chapter0301.cpp:23

2   breakpoint     keep y   0x080488eb in date_string() at chapter0301.cpp:7

3   breakpoint     keep y   0x08048a92 in main at chapter0301.cpp:27

4   breakpoint     keep y   0x080489ac in main at chapter0301.cpp:22

进入调试会话后,断点设置在每次调试结束就会自动清除,此时使用该命令查看应用程序调试断点没有任何信息。用户设置完断点之后,可以通过该命令查看所设置的断点基本信息,由断点编号、断点类型、地址以及断点内容组成。

需要清除某个断点时,可以使用相应的delete命令实现指定断点的删除,如删除main断点如下。

(gdb) break main

Breakpoint 1 at 0x80489bd: file chapter0301.cpp, line 23.

(gdb) delete 1

(gdb) info break

No breakpoints or watchpoints.

上述过程演示了删除调试会话中不需要的断点信息过程,直接使用gdb提供的delete命令指定需要删除的断点编号即可清除指定的断点,随后可以通过info命令直接查看当前会话中所有的断点信息。

对于当前应用程序调试过程中,开发者如果暂时想去掉某个断点不参与当前会话调试,可以使用gdb提供的disable命令来暂时实现断点禁用。基于上述过程,暂时需要禁用main函数处的断点操作如下。

(gdb) break main

Breakpoint 1 at 0x80489bd: file chapter0301.cpp, line 23.

(gdb) disable 1

(gdb) info break

Num Type           Disp Enb Address    What

1   breakpoint     keep n   0x080489bd in main at chapter0301.cpp:23

调试会话中,首先设置main函数断点信息,当整个调试会话中暂时不需要改断点参与调试时,可以使用gdb禁用断点功能。禁止断点命令为disable,后面依然指定为断点信息的编号,随后通过info命令查看当前断点信息可知,一旦禁用断点后断点信息相应的Enb会变为n。

相反,当用户在调试过程中又需要使用暂时被禁用的断点时,gdb提供enable命令来唤醒被禁用的断点,继续使其参与调试过程。上述被禁用的断点信息唤醒操作可以如下所示。

(gdb) info break

Num Type           Disp Enb Address    What

1   breakpoint     keep n   0x080489bd in main at chapter0301.cpp:23

(gdb) enable 1

(gdb) info break

Num Type           Disp Enb Address    What

1   breakpoint     keep y   0x080489bd in main at chapter0301.cpp:23

gdb工具中disable与enable命令功能相对,基本使用方法也相同,都是命令后跟相应的断点编号,即可实现相应的禁用以及唤醒断点的功能。上述演示过程首先通过info命令查看当前调试会话断点相关信息,随后根据其断点编号使用enable命令唤醒该断点,此时查看会看到断点信息Enb变化为y,表示断点当前有效。

五、  gdb查看相关信息

当程序调试中运行到指定断点之后,调试者通常需要查询程序中相关变量等基本信息,以此来验证程序在运行过程中是否按照预先设计的思路在处理。gdb工具中提供了一系列的命令功能来完成调试会话过程中,查看应用程序内部处理变量等信息。

1.查看信息命令info

首先介绍第一个gdb查看信息命令info,类似Linux系统本身shell提供的info命令功能。gdb调试工具同样可以使用该命令实现查看相关调试会话中的基本信息。如上小节介绍断点设置时,为查看当前会话中断点的基本信息而使用该命令。info命令在gdb调试会话中可以通过帮助信息来了解其使用方式以及在调试会话中具备查看哪些信息的功能。

q  info args                – 查看程序中函数参数信息

q  info breakpoints     – 查看当前调试会话中断点信息

q  info catch              – 查看当前调试程序中生效的异常句柄

q  info files                – 显示调试文件的信息

q  info functions         – 显示所有方法名信息

q  info macro             – 显示宏定义信息

q  info sharedlibrary   – 查看当前程序加载的共享库的信息

上述主要列出日常调试应用程序过程中info命令常用组合。不同的组合使用表示查看内容的不同。初学者可以通过调试简单的应用实例开始,逐个的通过实践相应的命令组合来加深调试程序中各个命令使用的印象。

2.查看程序信息print

另一个gdb调试提供的查询命令为print,该命令具备很强的查看程序信息功能。使用该命令调试者可以查看程序运行时任何有效的表达式情况。其中,有效表达式包含基本变量、数组、函数调用以及比较复杂的对象内容的查看。另外gdb还提供了查询相关变量的类型命令whatis,用于配合print命令查看相应变量具体信息,两个查看信息命令使用格式都为查看命令加查询对象表达式组合使用。

应用程序调试中最常见的print命令使用情形要数在程序处理中查阅各个变量的值,以便了解程序处理是否按照设想的处理思路在执行。本小节也主要介绍print命令查看程序中变量值的使用过程,该命令的其余功能可以根据初学者要求自行查阅帮助文档练习。

3.使用示例

上述info、print与whatis命令的使用信息可以通过gdb环境下的help命令查询获取。下面将会通过一个简单完整实例来演示info、print以及whatis命令在调试过程中的使用情况。该实例主要实现几种简单的类型数值计算功能,实例代码编辑如下所示。

//实例chapter0303

//chapter0303.h

#ifndef     CHAPTER0303_01_H

#define    CHAPTER0303_01_H

#include <iostream>

using namespace std;

/*整型加法运算声明*/

int sum(int value1, int value2);

/*整型减法运算*/

int sub(int value1, int value2);

/*整型乘法运算*/

int multi(int value1, int value2);

#endif

 

//chapter0303.cpp

#include "chapter0303.h"

/*整型加法运算定义*/

int sum(int value1, int value2)

{

         int reslut = 0;

         result  =  (value1 + value2);

         return result ;                                                                      //返回传入参数加法结果

}

/*整型减法运算定义*/

int sub(int value1, int value2)

{

         int reslut = 0;

         result  =  (value1 - value2);

         return result ;                                                                      //返回传入参数减法结果

}

/*整型乘法运算定义*/

int multi(int value1, int value2)

{

         int result = 0;

         result = (value1 * value2)

         return result;                                                                       //返回传入参数乘法结果

}

/*主函数入口*/

int main()

{

         int a,b,result;                                                                      //定义三个整型变量,分别表示运算数以及计算结果

    cout<<"Please input two values:"<<endl;                     //提示输入计算数据

    cin>>a>>b;                                                                         //从键盘输入数据

    result = sum(a,b);                                                              //调用加法运算方法,将计算结果放入结果变量

    cout<<"a and b sums:"<<result<<endl;                        //打印输出加法计算结果

    result = sub(a,b);                                                               //调用减法运算方法,将计算结果放入结果变量

    cout<<"a and b subs:"<<result<<endl;                          //打印输出减法计算结果

    result = multi(a,b);                                                             //调用乘法运算方法,将计算结果放入结果变量

    cout<<"a and b multis:"<<result<<endl;                       //打印输出乘法计算结果

    return 0;

}

Linux系统下需要编译源文件为chapter0303.cpp,为了添加gdb调试信息,因此编译时需要加上-g调试命令,该程序文件编译命令编辑如下所示。

g++ -g chapter0303.cpp –o chapter0303

当前shell下执行上述编译命令,生成可执行程序文件,执行该程序文件运行结果如下所示。

[developer @localhost src]$g++ -g chapter0303.cpp -o chapter0303

[developer @localhost src]cp chapter0303 ../bin

[developer @localhost src]$cd ../bin

[developer @localhost src]$./chapter0303

Please input two values:

12

11

a and b sums:23

a and b subs:1

a and b multis:132

实例chapter0303主要实现简单的整型变量计算功能,此处为了配合应用gdb调试命令info、print查看计算过程信息,因此简单的实现了整型变量的几种计算功能。从工程实现意义角度讲,此类运算应该考虑到计算类型的可变性,即可以同时支持不同类型的变量数据计算,后面讲述到C++模板应用时可以给出更具体的实现方式。

该实例实现细节非常的简单,主要由三个基本功能函数组成,函数接口的参数分别都是两个整型数,返回值也定义为整型。计算功能函数内部实现非常简单,仅仅通过return语句直接返回两数计算结果。主程序中定义三个整型变量分别表示参与计算的变量a与b,以及表示计算结果的变量result。随后分别调用计算功能函数,传入输入的整型变量并将计算结果放入结果变量中,最后打印输出查看计算结果。

实例程序在编译时已经添加-g调试信息命令,下面启动gdb调试工具,在调试该实例中演示info、print与whatis命令在实际应用中的情况,实例演示结果如下所示。

本调试实例根据上述程序编译生成的可执行程序,首先在当前Shell中运行gdb程序,启动信息如下所示。

[developer@localhost developer]$ gdb                                                   //在shell下执行gdb启动程序命令

GNU gdb Red Hat Linux (5.3post-0.20021129.18rh)                                     //以下为gdb工具启动信息

Copyright 2003 Free Software Foundation, Inc.

GDB is free software, covered by the GNU General Public License, and you are

welcome to change it and/or distribute copies of it under certain conditions.

Type "show copying" to see the conditions.

There is absolutely no warranty for GDB.  Type "show warranty" for details.

This GDB was configured as "i386-redhat-linux-gnu".

(gdb)                                                                                                                //gdb会话位置,供调试者执行命令

接着需要通过gdb程序提供的break命令操作设置程序中的断点信息,具体操作如下所示。

(gdb) break main                                                    //通过break命令设置主程序入口断点

Breakpoint 1 at 0x8048706: file chapter0303.cpp, line 27.

(gdb) break sum                                                      //通过break命令设置加法操作函数处断点

Breakpoint 2 at 0x80486a6: file chapter0303.cpp, line 5.

(gdb) break sub                                                       //通过break命令设置减法操作函数处断点

Breakpoint 3 at 0x80486c2: file chapter0303.cpp, line 12.

(gdb) break multi                                                     //通过break命令设置乘法操作函数处断点

Breakpoint 4 at 0x80486e0: file chapter0303.cpp, line 19.

 

开发者通过分析程序,选出程序中需要设置断点的位置,通过上述break命令设置完断点信息,下面将会通过info命令来查询断点信息设置是否成功,具体操作如下所示。

(gdb) info breakpoints                                            //通过info命令查看当前调试会话所有断点信息

Num Type           Disp Enb Address    What

1   breakpoint     keep y   0x08048706 in main at chapter0303.cpp:27

2   breakpoint     keep y   0x080486a6 in sum(int, int) at chapter0303.cpp:5

3   breakpoint     keep y   0x080486c2 in sub(int, int) at chapter0303.cpp:12

4   breakpoint     keep y   0x080486e0 in multi(int, int) at chapter0303.cpp:19

当前调试会话中使用info命令,设置查看的信息breakpoints表明是断点信息查看,断点设置信息都无误之后,在当前调试会话中将会通过run命令在当前调试会话中运行可执行程序,随后将会在第一个断点出运行停止,具体操作如下所示。

(gdb) run                                                                                      //通过run命令执行当前调试会下的可执行程序

Starting program: /home/ocs/users/wangfeng/Linux_c++/chapter03/chapter0303/bin/chapter0303

Breakpoint 1, main () at chapter0303.cpp:27                       //程序运行至第一个断点处停下

27          cout<<"Please input two values:"<<endl;     //打印下一行即将执行的代码

以上通过run命令在当前调试会话中运行程序,在第一个断点main函数处打印输出提示信息处停止运行。下一步将会通过next命令来实现gdb单步调试。同时配合print打印命令,查看单步调试过程中各个变量的内容。

(gdb) n                                                                       //通过next单步调试命令执行下一行代码

Please input two values:

28          cin>>a>>b;

(gdb) n                                                                       //继续执行单步调试

12                                                                               //输出程序计算中间结果

12                                                                               //输出程序计算中间结果

29          result = sum(a,b);

(gdb) print result                                                      //通过print命令查看中间变量值

$1 = 134514862

(gdb) n                                                                       //单步运行至第二个断点处

Breakpoint 2, sum(int, int) (value1=12, value2=12) at chapter0303.cpp:5

5           int reslut = 0;

(gdb) n                                                                       //单步运行至变量相加代码处

6           reslut = (value1 + value2);

(gdb) print result                                                      //打印结果变量值

$2 = 0                                                                         //此时还未执行上述代码,因此打印处变量默认值0

(gdb) print (value1 + value2)                                 //通过print打印表达式计算结果

$3 = 24

(gdb) n

7           return reslut;

(gdb) print result                                                      //当执行完毕上述表达式计算代码后,打印结果变量,输出其值

$4 = 24

上述过程中通过next命令将程序执行到从键盘输入变量的代码处,继续通过next命令执行程序下一步,输出了输入的两个变量值,同时提示执行至将两个变量实现相加的代码处。随后通过print命令打印result变量的内容值,由于还没正式执行该部分变量相加调用,因此此时的result变量中存放的是一个未知数的值。

再通过next命令执行程序下一步,执行到第二个断点处,调用到sum函数实现a、b两个变量的相加操作,同时显示下一步需要执行的代码,是result变量的定义。继续执行next命令,运行至两个变量实际计算表达式处,此时还未执行正式的计算,因此通过print打印输出的result变量值依然为初始的0值。

随后通过print命令直接打印了表达式中两个变量之和的计算结果,显示结果为24,因为输入的两个计算变量都为12。继续next执行下一步程序,定位至返回计算结果变量result代码处,此时通过print打印输出result变量信息,为两个变量计算的之和。

(gdb) whatis result                                                   //通过whatis命令查看当前变量的类型

type = int

(gdb) c                                                                       //通过continue命令继续程序的执行,直到最后输出结果

Continuing.

a and b sums:24

最后通过whatis命令查看result变量的类型,直接whatis紧跟着为查询变量名即可。查询结果可知sum函数内部变量result为整型。通过continue命令继续执行程序,加法函数内部根据传入的实参计算完毕后返回,主程序中打印输出结算结果,因此上述调试最后显示a与b相加结果为24。

六、  gdb单步调试

gdb中单步调试程序主要表现在程序运行中需要一步步确认运行过程是否正确的场合下,方便开发者调试程序运行中的一些细节问题。

gdb工具提供单步调试共有两种命令,即step与next,两种命令都可以用于在当前调试会话中单步执行调试程序。两个命令使用的基本语法形式基本相同,大致操作如下所示。

(gdb) n                                  //next命令简写

(gdb) next                                      //next命令全名称

(gdb) s                                  //step命令简写

(gdb) step                                      //step命令全名称

并且两种命令后都可以跟相应的参数,表示执行到当前程序后的几行,不设定任何参数时默认情况表示单行,即命令执行一次程序运行到下一行代码。

两种同样可以实现单步调试功能的命令虽然功能相同,但在执行细节上存在一定的区别。对于单步调试命令step来讲,除了单步执行程序之外,当遇到函数调用时会单步进入该函数内部执行。而不同的是next命令遇到函数调用通常不会进入函数内部执行。

上小节实例chapter0303中,通过next命令可以查看到函数内部的代码执行情况,那是因为之前已经通过设置断点信息,在执行的时候往往都直接定位至断点处的下一行代码。因此通过next命令可以在断点后单步的查看程序执行情况。

为了区分清楚step与next命令之间的差别,依然采用chapter0301的实例,但是在调试前不设置断点,该调试演示如下所示。

//step命令演示

(gdb) break main                                                                                 //设置主程序入口处断点

Breakpoint 1 at 0x8048706: file chapter0303.cpp, line 27.

(gdb) r                                                                                                 //当前调试会话中执行程序

Starting program: /home/ocs/users/wangfeng/Linux_c++/chapter03/chapter0303/bin/chapter0303

 

Breakpoint 1, main () at chapter0303.cpp:27                                //程序运行至主程序入口断点处停下

27          cout<<"Please input two values:"<<endl;

(gdb) step                                                                                                       //通过step单步调试程序

Please input two values:

28          cin>>a>>b;

(gdb) step                                                                                                       //继续单步执行程序

12

12

29          result = sum(a,b);

(gdb) step                                                                                                       //通过step单步调试命令可进入函数内部跟踪调试

sum(int, int) (value1=12, value2=12) at chapter0303.cpp:5

5           int reslut = 0;

//next命令演示

(gdb) break main                                                                                 //设置主程序入口处断点

Breakpoint 1 at 0x8048706: file chapter0303.cpp, line 27.

(gdb) r                                                                                                    //当前调试会话中执行程序

Starting program: /home/ocs/users/wangfeng/Linux_c++/chapter03/chapter0303/bin/chapter0303

 

Breakpoint 1, main () at chapter0303.cpp:27                                //运行至断点处程序停下

27          cout<<"Please input two values:"<<endl;

(gdb) n                                                                                                   //通过next单步执行程序

Please input two values:

28          cin>>a>>b;

(gdb) n                                                                                                   //继续单步执行程序

12

12

29          result = sum(a,b);

(gdb) n                                                                                                   //next单步调试命令遇到函数调用并不进入其中

30          cout<<"a and b sums:"<<result<<endl;                            //与step单步调试的最大区别

(gdb) n

a and b sums:24

31          result = sub(a,b);

上述调试演示可以看出,step命令用于单步需要进入调用函数内部执行的情况,而next命令则可以用于正常情况下的单步调试。初学者可以在该类命令后添加执行调试代码行数,练习该类命令使用情况。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: