gdb调试的基本用法

gdb调试的基本用法

gdb是GNU开源组织发布的一个强大的UNIX下的程序调试工具。

一般来说,gdb主要帮忙你完成下面四个方面的功能:

  • 启动你的程序,可以按照你的自定义的要求随心所欲的运行程序。
  • 可让被调试的程序在你所设置指定的断点处停住。(断点可以是条件表达式)
  • 当程序被停住时,可以检查此时你的程序中所发生的事。
  • 动态的改变你程序的执行环境。

从上面看来,gdb和一般的调试工具没有什么两样,基本上也是完成这些功能,不过在细节上,你会发现gdb这个调试工具的强大,大家可能比较习惯了图形化的调试工具,但有时候,命令行的调试工具却有着图形化工具所不能完成的功能。让我们现在一一看来。

 

首先我们要了解到程序有两个版本,Debug版本和Release版本,

  • Debug版本可以称为“可调试版本”,它包含了大量的调试信息,并且不进行任何优化,便于程序员调试。Debug模式下生成两个文件,除了生成.exe或.dll文件外,而且生成一个.pdb文件,该文件记录了代码中断点等调试信息。(调试信息是在编译阶段就要添加到文件中的)
  • Release版本可以成为“发行版本”,因为它是面对用户的,所以它不需要包含任何调试信息,且在编译期间对程序进行优化,使得程序在代码大小和运行速度上都是最优的。Release模式下生成一个.exe或.dll文件。

 

调试的对象:Debug版本的可执行文件

gdb Debug版本的可执行文件

参数列表:

命令

简写

说明

list

l

显示main函数的所在的文件的源代码

 

list:num

显示指定行的源代码

 

ist  first,last

显示从first行到last行之间的源代码

break

b

设置断点,程序运行到断点处会停下来

 

b linenum

在linenum行添加一个断点

 

info b

查看断点信息

 

delete d

删除断点

run

r

开始启动程序

next

n

单条语句执行,如果该语句为函数调用,不会进入函数内部执行

step

s

执行下一条指令,如果该语句为函数调用,则进入函数内部执行其第一条语句

finish

 

退出当前函数

continue

c

继续程序的运行,退出当前断点,直到遇到下一个断点

set var name=values

 

在程序运行中动态改变变量(name)的值为(values)

print

p

打印内部变量值

 

p valname

查看变量的值

 

p &valname

查看变量的地址

 

p arrname

查看数组所有的元素值

 

p *parr@len

通过指向数组的指针查看数组中指定长度的元素的值,你可以使用GDB的“@”操作符,“@”的左边是第一个内存的地址的值,“@”的右边则你你想查看内存的长度。)

display

disp

跟踪查看某个变量,每次停下来都会打印它的值

 

display valname

查看指定的变量,每当一条指令执行完时,显示一次追踪的变量的值

 

info display

查看追踪信息

 

undisplay dpnum

取消被追踪的变量,编号为dpnum

backtrace

bt

显示函数调用过程(堆栈)

until

u

当厌倦了在一个循环体内单步跟踪时,这个命令可以运行程序直到退出循环体

quit

q

退出调试,退出GDB环境

 

我们逐一来看一看这些命令如何使用:

这是一个调试示例:

 

#include 

int fun(int n)
{
	int sum = 0;
	int i = 0;

	for(i; i <= n; i++)
	{
		sum += i;
	}

	return sum;
}


int main()
{
	int i = 0;
	long result = 0;

	for(i = 1; i <= 100; i++)
	{
		result += i;
	}

	printf("result[1-100] = %d\n", result);
	printf("result[1-250] = %d\n", fun(250));

	return 0;
}

(1)启动gdb调试:

gdb调试的基本用法_第1张图片

 

(2)调试第一个命令“l ---> list”从主函数附近开始列出源码:

gdb调试的基本用法_第2张图片

 

(3)如果找的是第8行附近的代码“list :8”:

gdb调试的基本用法_第3张图片

 

(4)直接回车,表示重复上一条指令:

gdb调试的基本用法_第4张图片

 

(5)调试第二个命令“b ---> break”在指定行设置断点:

 

(6)在函数fun()入口处设置断点(设置在fun函数中的第一行代码处):

gdb调试的基本用法_第5张图片

 

(7)查看断点信息“info break或者info b”:

 

(8)调试第三个命令“r ---> run”运行程序,从主函数开始在第一个断点处停止:

 

(9)调试第四个命令“n ---> next”单条语句执行:

 

(10)调试第五个命令“c ---> continue”继续运行程序:

gdb调试的基本用法_第6张图片

 

(11)调试第六个命令“p ---> print”打印变量i的值:

gdb调试的基本用法_第7张图片

 

 

(12)调试第七个命令“bt ---> backtrace”查看函数堆栈:

 

(13)调试第八个命令“finish”退出函数:

 

(14)c,继续运行:

gdb调试的基本用法_第8张图片

 

(15)调试第九个命令“quit”退出调试,退出GDB环境:

gdb调试的基本用法_第9张图片

 

好了,上述只是我们常用的一些指令应用时的场景,接下来我们要系统的认识一下gdb了:

①:就上述而言,gdb调试需要的是Debug版本的可执行文件,而不需要Release版本的,

所以我们编译阶段要注意加上调试信息,使用编译器的-g参数可以做到这一点。

如在编译阶段时,加入调试信息(gcc -c main.c -g

或者一步生成可执行文件时加上调试信息(gcc -o main main.c -g

如果没有-g,你将看不见程序的函数名、变量名,所代替的全是运行时的内存地址。

 

②:gdb中,输入命令时,可以不用打全命令,只用打命令的前几个字符就可以了,当然,命令的前几个字符应该要标志着一个唯一的命令,在Linux下,你可以敲击两次TAB键来补齐命令的全称,如果有重复的,那么gdb会把其例出来。

 

③:如果你觉得已定义好的停止点没有用了,你可以使用delete、clear、disable、enable这几个命令来进行维护。

比删除更好的一种方法是disable停止点,disable了的停止点,GDB不会删除,当你还需要时,enable即可,就好像回收站一样。

 

④:一般来说,GDB会根据变量的类型输出变量的值。但你也可以自定义GDB的输出的格式。例如,你想输出一个整数的十六进制,或是二进制来查看这个整型变量的中的位的情况。要做到这样,你可以使用GDB的数据显示格式:

  • x 按十六进制格式显示变量。
  • d 按十进制格式显示变量。
  • u 按十六进制格式显示无符号整型。
  • o 按八进制格式显示变量。
  • t 按二进制格式显示变量。
  • a 按十六进制格式显示变量。
  • c 按字符格式显示变量。
  • f 按浮点数格式显示变量。

例如:(打印变量i的值)

(gdb) p i (默认十进制)

$21 = 101

(gdb) p/a i(按十六进制格式显示变量)

$22 = 0x65

(gdb) p/c i(按字符格式显示变量)

$23 = 101 ''e''

(gdb) p/f i(按浮点数格式显示变量)

$24 = 1.41531145e-43

(gdb) p/x i(按十六进制格式显示变量)

$25 = 0x65

(gdb) p/t i(按二进制格式显示变量)

$26 = 1100101

 

⑤:查看内存,你可以使用examine命令(简写是x)来查看内存地址中的值。

命令:x/3uh 0x54320 表示,从内存地址0x54320读取内容,h表示以双字节为一个单位,3表示三个单位,u表示按十六进制显示。

 

⑥:gcc编译.cpp文件时:(记得链接上C++库)

gcc -o main filelist(*.cpp) -lstdc++

 

总结:GDB是一个强大的命令行调试工具。大家知道命令行的强大就是在于,其可以形成执行序列,形成脚本。UNIX下的软件全是命令行的,这给程序开发提代供了极大的便利,命令行软件的优势在于,它们可以非常容易的集成在一起,使用几个简单的已有工具的命令,就可以做出一个非常强大的功能。这是图形化工具所不能及的。

你可能感兴趣的:(Linux)