加入调试信息
要调试C/C++的程序,首先在编译时,我们必须要把调试信息加到可执行文件中。使用编译器(cc/gcc/g++)的 -g 参数可以做到这一点。如:
gcc -g hello.c -o hello
g++ -g hello.cpp -o hello
如果没有-g,你将看不见程序的函数名、变量名,所代替的全是运行时的内存地址。
启动GDB
1、gdb
program也就是你的执行文件,一般在当前目录下。
2、gdb core
用gdb同时调试一个运行程序和core文件,core是程序非法执行后core dump后产生的文件。
在GDB中运行程序
当以gdb 方式启动gdb后,gdb会在PATH路径和当前目录中搜索的源文件,可使用l或list命令,看看gdb是否能读到源代码。
在gdb中,运行程序使用r或是run命令。程序的运行,你有可能需要设置下面四方面的事。
1、程序运行参数。
set args 可指定运行时参数。(如:set args 10 20 30 40 50)
show args 命令可以查看设置好的运行参数。
2、运行环境。
path <dir> 可设定程序的运行路径。
show paths 查看程序的运行路径。
set environment varname [=value] 设置环境变量。如:set env USER=hchen
show environment [varname] 查看环境变量。
3、工作目录。
cd <dir> 相当于shell的cd命令。
pwd 显示当前的所在目录。
4、程序的输入输出。
info terminal 显示你程序用到的终端的模式。
使用重定向控制程序输出。如:run > outfile
暂停程序执行
在gdb中,我们可以有以下几种暂停方式:断点(BreakPoint)、观察点(WatchPoint)、捕捉点(CatchPoint)、信号(Signals)、线程停止(Thread Stops)。如果要恢复程序运行,可以使用c或是continue命令。
当进程被gdb停住时,你可以使用info program 来查看程序的是否在运行,进程号,被暂停的原因。
断点
一、设置断点(BreakPoint)
我们用break命令来设置断点。正面有几点设置断点的方法:
break
在进入指定函数时停住。C++中可以使用class::function或function(type,type)格式来指定函数名。
break
在指定行号停住。
break +offset
break -offset
在当前行号的前面或后面的offset行停住。offiset为自然数。
break filename:linenum
在源文件filename的linenum行处停住。
break filename:function
在源文件filename的function函数的入口处停住。
break *address
在程序运行的内存地址处停住。
break
break命令没有参数时,表示在下一条指令处停住。
break … if <condition>…可以是上述的参数,condition表示条件,在条件成立时停住。比如在循环境体中,可以设置break if i=100,表示当i为100时停住程序。
查看断点时,可使用info命令,如下所示:(注:n表示断点号)
info breakpoints [n]
info break [n]
观察点
观察点一般来观察某个表达式(变量也是一种表达式)的值是否有变化了,如果有变化,马上停住程序。我们有下面的几种方法来设置观察点:
watch <expr> 为表达式(变量)expr设置一个观察点。一旦表达式值有变化时,马上停住程序。
rwatch <expr> 当表达式(变量)expr被读时,停住程序。
awatch <expr> 当表达式(变量)的值被读或被写时,停住程序。
info watchpoints 列出当前所设置了的所有观察点。
恢复程序运行和单步调试
当程序被停住了,你可以用continue命令恢复程序的运行直到程序结束,或下一个断点到来。也可以使用step或next命令单步跟踪程序。
continue [ignore-count]
c [ignore-count]
fg [ignore-count]
恢复程序运行,直到程序结束,或是下一个断点到来。ignore-count表示忽略其后的断点次数。continue,c,fg三个命令都是一样的意思。
step <count>(单步跟踪),如果有函数调用,他会进入该函数。进入函数的前提是,此函数被编译有debug信息。很像VC等工具中的step in。后面可以加count也可以不加,不加表示一条条地执行,加表示执行后面的count条指令,然后再停住。
next (单步跟踪) ,如果有函数调用,他不会进入该函数。很像VC等工具中的step over。后面可以加count也可以不加,不加表示一条条地执行,加表示执行后面的count条指令,然后再停住。
set step-mode on(打开step-mode模式),于是,在进行单步跟踪时,程序不会因为没有debug信息而不停住。这个参数有很利于查看机器码。
set step-mod off:关闭step-mode模式。
finish运行程序,直到当前函数完成返回。并打印函数返回时的堆栈地址和返回值及参数值等信息。
until 或 u当你厌倦了在一个循环体内单步跟踪时,这个命令可以运行程序直到退出循环体。
stepi 或 si
nexti 或 ni
单步跟踪一条机器指令!一条程序代码有可能由数条机器指令完成,stepi和nexti可以单步执行机器指令。与之一样有相同功能的命令是“display/i $pc” ,当运行完这个命令后,单步跟踪会在打出程序代码的同时打出机器指令(也就是汇编代码)
显示源代码
GDB 可以打印出所调试程序的源代码,当然,在程序编译时一定要加上-g的参数,把源程序信息编译到执行文件中。不然就看不到源程序了。当程序停下来以后,GDB会报告程序停在了那个文件的第几行上。你可以用list命令来打印程序的源代码。
list <linenum> | 显示程序第linenum行的周围的源程序 |
---|---|
list <function> | 显示函数名为function的函数的源程序 |
list | 显示当前行后面的源程序 |
list - | 显示当前行前面的源程序 |
set listsize <count> | 设置一次显示源代码的行数 |
show listsize | 查看当前listsize的设置 |
list <first>, | 显示从first行到last行之间的源代码 |
list , <last> | 显示从当前行到last行之间的源代码 |
list + | 往后显示源代码 |
一般是打印当前行的上5行和下5行,如果显示函数是是上2行下8行,默认是10行,当然,你也可以定制显示的范围,使用以上最后命令可以设置一次显示源程序的行数。
一般来说在list后面可以跟以下这些的参数:
<linenum> 行号。
<+offset> 当前行号的正偏移量。
<-offset> 当前行号的负偏移量。
<filename:linenum> 哪个文件的哪一行。
<function> 函数名。
<filename:function> 哪个文件中的哪个函数。
<*address> 程序运行时的语句在内存中的地址。
查看运行时数据
在你调试程序时,当程序被停住时,你可以使用print命令(简写命令为p),或是同义命令inspect来查看当前程序的运行数据。print命令的格式是:
print <expr>
print <f> <expr>
<expr>是表达式,是你所调试的程序的语言的表达式(GDB可以调试多种编程语言),<f>是输出的格式,比如,如果要把表达式按16进制的格式输出,那么就是/x。
一、表达式
print和许多GDB的命令一样,可以接受一个表达式,GDB会根据当前的程序运行的数据来计算这个表达式,既然是表达式,那么就可以是当前程序运行中的const常量、变量、函数等内容。可惜的是GDB不能使用你在程序中所定义的宏。
10页
help命令
gdb所支持命令可以使用help命令来查看
gdb的命令很多,gdb把之分成许多个种类。help命令只是例出gdb的命令种类,如果要看种类中的命令,可以使用help 命令,如:help breakpoints,查看设置断点的所有命令。也可以直接help 来查看命令的帮助。
使用shell命令
在gdb环境中,你可以执行UNIX的shell的命令,使用gdb的shell命令来完成:
shell <command string>
调用UNIX的shell来执行,环境变量SHELL中定义的UNIX的shell将会被用来执行,如果SHELL没有定义,那就使用UNIX的标准shell:/bin/sh。(在Windows中使用Command.com或cmd.exe)
make命令
make <make-args>
可以在gdb中执行make命令来重新build自己的程序。这个命令等价于“shell make ”
按回车会直接执行上一条命令
r用来运行程序,如果设置了断点,将会在断点处停止。(running的缩写)
p i显示变量i的当前值,print的缩写
gdb跟踪core
当我们发生段错误之后不要慌,可以通过该方式来直接定位段错误发生的位置,生成的core相当于案发现场,就好比出车祸了,警察过来记录一样。
步骤如下:
- ulimit -c查看core文件大小
- 设置生成core:ulimit -c unlimited
- 取消生成core:ulimit -c 0
- 打开方式 gdb app core
- 打开后会直接显示错误地点,如果没有,可以使用where来告诉错误在哪里
这里我们会发现一个问题,就是所有生成的core文件都叫做“core”,我们可不可以修改呢?答案是肯定的,方式如下:设置core文件格式:/proc/sys/kernel/core_pattern 文件不能用vi,可以用后面的套路 echo “/corefile/core-%e-%p-%t”>core_pattern
查看出现段错误时函数的调用过程(轨迹)backtrace