1.认识GDB调试
在linux搞程序的时候我们往往要用到GDB调试在这期间我们会涉及到对多线程,宏,源文件等得调试,这时候我们会感到在linux中使用GDB的强大。
首先我们开始调试之前,必须用程序中的调试信息编译要调试的程序。这样,gdb 才能够调试所使用的变量、代码行和函数。如果要进行编译,请在 gcc(或 g++)下使用额外的 '-g' 选项来编译程序:gcc -g main.c -o main
如果没有-g,你将看不见程序的函数名、变量名,所代替的全是运行时的内存地址。当你用-g把调试信息加入之后,并成功编译目标代码以后,让我们来看看如何用gdb来调试他.
一般来说,GDB主要帮忙你完成下面四个方面的功能:
1、启动你的程序,可以按照你的自定义的要求随心所欲的运行程序。
2、可让被调试的程序在你所指定的调置的断点处停住。(断点可以是条件表达式)
3、当程序被停住时,可以检查此时你的程序中所发生的事。
4、动态的改变你程序的执行环境。
2.罗列一些常见的GDB命令
关于断点的命令:
awatch 用来为一个表达式设置观察点,在表达式的值发生改变时,或者是当表达式的被度曲的时候,程序都会停止运行。
格式:awatch 要设置观察点的表达式
break 用来设置断点。
格式:break 要设置断点的行号
clear 清除断点。
格式:clear 要清楚的断点所在的行号
commands 在遇到断点之后执行特定指令而设。
格式:commands 断点号。
condition 在满足一定条件时才在指定的行上设置断点。
格式:condition 断点编号 条件表达式
delete 清楚断点或自动显示的表达式。
格式:delete 断点的编号或者表达式
disable 使断点暂时失效。
格式:disable 断点编号
enable 恢复暂时失效的断点。
格式:enable 断点编号
ignore 在一定范围内,忽略用户设置的断点。
格式:ignore N CONT
tbreak 设置临时断点。作用一次。
格式:tbreak 设置临时断点的行号
watch 为一个表达式设置观察点。当表达式的值发生改变时,程序就会停止运行。
格式:watch 要设置观察点的表达式
关于数据的命令:
display 用来显示一些表达式的值
格式:display 要显示值的表达式
info display 显示当前所有的要显示值得表达式的有关情况。
格式:info display
delete display 删除一个要显示值得表达式。
格式:delete display 删除显示的表达式的编号
disable display 暂时屏蔽那些不需要显示的表达式。
格式:disalbe display 屏蔽显示的表达式的编号
enable display 使显示值被屏蔽的表达式恢复显示。
格式:enable display 需要显示的表达式的编号
undisplay 结束某个表达式值的显示。
格式:undisplay 不需要再显示值的表达式
whatis 显示某个表达式的数据类型。
格式:whatis 需要查询类型的表达式
print 打印表达式值,也可以用来打印内存中从某个变量开始的一段区域的内容。
格式:print 需打印的表达式
ptype 用来给出类型定义
格式:ptype 参数
set 用来为变量赋值的
格式:set 变量=表达式
关于文件的命令:
add-shared-symbol-files 用来从动态的连接映射的共享目标文件中装入符号表。
add-symbol-file 用来从已经动态装入的文件中装入符号表。
格式:add-symbol-file FILE ADDR
cd 用来改变当前工作目录的。
core-file 使某个文件成为core dump,从而可以检查内存和寄存器。
directory 用来向源文件搜索路径中增加一个目录。
格式:directory 要增加的目录
file 命令是用来装入待调试程序的命令。
格式:file 要装入的文件的名称
list 用来进行文件内容列表
list命令可使用的非空的参数有以下几种方式:
LINENUM:当前文件的LINENUM行;
FILE:LINENUM:指定文件的LINENUM行;
FUNCTION:当前文件的FUNCTION函数;
FILE:FUNCTION:指定文件的FUNCTION函数;
*ADDRESS:列出包含该地址的文件。
格式:list 要列表的开始行号
技巧:在GDB提示符下按回车键会执行上一个命令
forward 用来从列表当前行开始向后查找第一个匹配某个字符串的程序行。
格式:forward 要匹配的字符串
load 用来动态的往正在调试的程序中装入文件,并记录它的符号表,准备连接。
格式:load 准备装入的文件名称
path 用来向目标文件的搜索路径中增加目录的。
格式:path 要增加到搜索路径中的目录名称
reverse-search 从列表当前行开始向前查找第一个匹配某个字符串的程序行。
search 和forward 命令的用法是完全一致的。
关于程序运行的命令:
cont 使程序字信号发生后或是停在断点之后再继续运行。
handle 用来对信号设置处理函数的。
格式:handle 信号 信号处理
jump 用来指定程序开始调试的指令或地址的。
格式:jump 行号或是指令地址
kill 用来结束当前程序的调试。
格式:kill
next 用来继续程序的运行的,它越过子程序调用。
格式:next N 或是空
nexti 用来单步执行一条指令的。
step 用来执行一条语句,它也不越过子程序的调用,而是跟踪到子程序内部。
stepi 用来执行一条指令,它也不越过子程序的调用,而是跟踪到子程序内部。
关于堆栈的命令:
backtrace 用来打印栈桢(stack frame)指针的,他的使用格式和功能和比命令完全相同
。
格式:backtrace 要打印出来的栈桢指针的个数
frame 用来打印栈桢的。
格式:frame 要打印的栈桢的编号
select-frame 用来指定要选择的栈桢的编号。
_________________________________________________________
GDB用法小结
1. gdb exe
使得exe程序运行在debug环境下
2. break functiona
在functiona函数处设置端点
3. run
让程序从main入口执行到断点functiona
4. n
next,单步执行,相当于VC中的调试命令step over
5. s
step into,进入子函数,察看子函数的执行情况
6. bt
backtrace查看堆栈的情况
7. p variant
print出变量variant的值
8. l
list命令,查看当前的行的上下文,默认显示10行
9. p variant=correct value
如果发现此时的variant的值不正确,我们可以给variant设置一个正确的值(correct value)
然后,用第10项中的命令继续执行
10. c
continue 继续执行,可以是经过按照更改后的值继续执行。相当于VC中的F5
11. quit or Ctrl+C
退出gdb
在gdb的命令行下,可以通过file exeprogram 载入要debug的文件
gdb -silent 表示不提示GDB的版权信息 or gdb -q (quiet)
gdb -h 显示gdb的帮助
12 About Help
gdb>help
apropos args //查找所有的GDB命令以及它的文档中包含args的表达式
complete i //列出所有以i开头的gdb命令
针对某一个命令的帮助是help command ,例如help info
显示info的用法,info 可以查看args,breakpoints,stack......
show命令只要是显示gdb的信息,如show version
13 break
break function
在某一个函数的地方设置端点
break linenum
在确定的某一行的地方设置断点
break +offset
-offset
break *address在某一个地址设置断点
14 watch
watch expr
查看某一个表达式
rwatch expr
查看某一个表达式,并在读入该表达式的时候,设置断点
15 查看源代码
list lineNum 在lineNum的前后源代码显示出来
list + 列出当前行的后面代码行
list - 列出当前行的前面代码行
list function
set listsize count
设置显示代码的行数
show listsize
显示打印代码的行数
list first,last
显示从first到last的源代码行
16 编辑源代码
edit 编辑当前所在的行
edit num
edit function 编辑包含函数定义的文件
edit filename:function
设置编辑器
EDITOR=/usr/bin/vi
export EDITOR
gdb ....
实例函数:
代码示例 main.c
#include
int wib(int no1, int no2)
{
int result, diff;
diff = no1 - no2;
result = no1 / diff;
return result;
}
int main(int argc, char *argv[])
{
int value, div, result, i, total;
value = 10;
div = 6;
total = 0;
for(i = 0; i < 10; i++)
{
result = wib(value, div);
total += result;
div++;
value--;
}
printf("%d wibed by %d equals %dn", value, div, total);
return 0;
}
这个程序将运行 10 次 for 循环,使用 wib() 函数计算出累积值,最后打印出结果。
3.core文件(可以用来快速定位出现错误的位置)
在 gdb 下运行程序可以使俘获错误变得更容易,但在调试器外运行的程序通常会中止而只留下一个 core 文件。gdb 可以装入 core 文件,并让您检查程序中止之前的状态。
比如一个可执行的文件 main;
在 gdb 外运行示例程序 main 将会导致核心信息转储:
$ ./main
Floating point exception (core dumped)
要使用 core 文件启动 gdb,在 shell 中发出命令 'gdb main core' 或 'gdb main -c core'。gdb 将装入 core 文件,main 的程序清单,显示程序是如何终止的,并显示非常类似于我们刚才在 gdb 下运行程序时看到的消息:
.......
Core was generated by `./main.
Program terminated with signal 8, Floating point exception.
#0 0x80483ea in wib (no1=8, no2=8) at main.c:7 //wib()是个函数
7 result = no1 / diff; //函数中的第七行出现错误
此时,可以发出 'info locals'、'print'、'info args' 和 'list' 命令来查看引起除数为零的值。'info variables' 命令将打印出所有程序变量的值,但这要进行很长时间,因为 gdb 将打印 C 库和程序代码中的变量。为了更容易地查明在调用 wib() 的函数中发生了什么情况,可以使用 gdb 的堆栈命令。
3.gdb 的堆栈命令
为了更容易地查明在调用 wib() 的函数中发生了什么情况,可以使用 gdb 的堆栈命令。
程序“调用堆栈”是当前函数之前的所有已调用函数的列表(包括当前函数)。每个函数及其变量都被分配了一个“帧”,最近调用的函数在 0 号帧中(“底部”帧)。要打印堆栈,发出命令'bt'('backtrace' [回溯] 的缩写):
(gdb) bt
#0 0x80483ea in wib (no1=8, no2=8) at eg1.c:7
#1 0x8048435 in main (argc=1, argv=0xbffff9c4) at eg1.c:21
此结果显示了在 main() 的第 21 行中调用了函数 wib()(只要使用 'list 21' 就能证实这一点),而且 wib() 在 0 号帧中,main() 在 1 号帧中。由于 wib() 在 0 号帧中,那么它就是执行程序时发生算术错误的函数。
实际上,发出 'info locals' 命令时,gdb 会打印出当前帧中的局部变量,缺省情况下,这个帧中的函数就是被中断的函数(0 号帧)。可以使用命令 'frame' 打印当前帧。要查看 main 函数(在1 号帧中)中的变量,可以发出 'frame 1' 切换到 1 号帧,然后发出 'info locals' 命令:
(gdb) frame 1
#1 0x8048435 in main (argc=1, argv=0xbffff9c4) at eg1.c:21
21 result = wib(value, div);
(gdb) info locals
value = 8
div = 8
result = 4
i = 2
total = 6
此信息显示了在第三次执行 "for" 循环时(i 等于 2)发生了错误,此时 "value" 等于 "div"。
可以通过如上所示在 'frame' 命令中明确指定号码,或者使用 'up' 命令在堆栈中上移以及 'down' 命令在堆栈中下移来切换帧。要获取有关帧的进一步信息,如它的地址和程序语言,可以使用命令 'info frame'。
gdb 堆栈命令可以在程序执行期间使用,也可以在 core 文件中使用,因此对于复杂的程序,可以在程序运行时跟踪它是如何转到函数的。
4.连接到其它进程
除了调试 core 文件或程序之外,gdb还可以连接到已经运行的进程(它的程序已经过编译,并加入了调试信息),并中断该进程。只需用希望 gdb 连接的进程标识替换 core 文件名就可以执行此操作。以下是一个执行循环并睡眠的示例程序:
eg2 示例代码
#include
int main(int argc, char *argv[])
{
int i;
for(i = 0; i < 60; i++)
{
sleep(1);
}
return 0;
}
使用 gcc -g eg2.c -o eg2 编译该程序并使用 ./eg2 & 运行该程序。请留意在启动该程序时在背景上打印的进程标识,在本例中是 1283:./eg2 &[3] 1283
启动 gdb 并指定进程标识,在我举的这个例子中是 gdb eg2 1283。gdb 会查找一个叫作 "1283" 的 core 文件。如果没有找到,那么只要进程 1283 正在运行(在本例中可能在 sleep() 中),gdb 就会连接并中断该进程:
...
/home/seager/gdb/1283: No such file or directory.
Attaching to program: /home/seager/gdb/eg2, Pid 1283
...
0x400a87f1 in __libc_nanosleep () from /lib/libc.so.6
(gdb)
此时,可以发出所有常用 gdb 命令。可以使用backtrace 来查看当前位置与 main()的相对关系,以及 mian() 的帧号是什么,然后切换到 main() 所在的帧,查看已经在"for" 循环中运行了多少次:
(gdb) backtrace
#0 0x400a87f1 in __libc_nanosleep () from /lib/libc.so.6
#1 0x400a877d in __sleep (seconds=1) at ../sysdeps/unix/sysv/linux/sleep.c:7
8
#2 0x80483ef in main (argc=1, argv=0xbffff9c4) at eg2.c:7
(gdb) frame 2
#2 0x80483ef in main (argc=1, argv=0xbffff9c4) at eg2.c:7
7 sleep(1);
(gdb) print i
$1 = 50
如果已经完成了对程序的修改,可以 detach 命令继续执行程序,或者 kill 命令杀死进程。还可以首先使用 file eg2 装入文件,然后发出 attach 1283 命令连接到进程标识 1283 下的 eg2。
其它小技巧
gdb 可以让您通过使用 shell 命令在不退出调试环境的情况下运行 shell 命令,调用形式是 shell [commandline],这有助于在调试时更改源代码。
最后,在程序运行时,可以使用 set 命令修改变量的值。在 gdb 下再次运行 eg1,使用命令break 7 if diff==0 在第 7 行(将在此处计算结果)设置条件断点,然后运行程序。当 gdb 中断执行时,可以将 "diff" 设置成非零值,使程序继续运行直至结束:
Breakpoint 1, wib (no1=8, no2=8) at eg1.c:7
7 result = no1 / diff;
(gdb) print diff
$1 = 0
(gdb) set diff=1
(gdb) continue
Continuing.
0 wibed by 16 equals 10
Program exited normally.
5.多线程调试
多线程调试可能是问得最多的。其实,重要就是下面几个命令:
info thread 查看当前进程的线程。
thread <ID> 切换调试的线程为指定ID的线程。
break file.c:100 thread all 在file.c文件第100行处为所有经过这里的线程设置断点。
set scheduler-locking off|on|step,这个是问得最多的。
在使用step或者continue命令调试当前被调试线程的时候,其他线程也是同时执行的,怎么只让被调试程序执行呢?通过这个命令就可以实现这个需求。 off 不锁定任何线程,也就是所有线程都执行,这是默认值。 on 只有当前被调试程序会执行。 step 在单步的时候,除了next过一个函数的情况(熟悉情况的人可能知道,这其实是一个设置断点然后continue的行为)以外,只有当前线程会执行。
线程调试命令
(gdb)info threads
显示当前可调试的所有线程,每个线程会有一个GDB为其分配的ID,后面操作线程的时候会用到这个ID。
前面有*的是当前调试的线程。
(gdb)thread ID
切换当前调试的线程为指定ID的线程。
(gdb)thread apply ID1 ID2 command
让一个或者多个线程执行GDB命令command。
(gdb)thread apply all command
让所有被调试线程执行GDB命令command。
(gdb)set scheduler-locking off|on|step
估计是实际使用过多线程调试的人都可以发现,在使用step或者continue命令调试当前被调试线程的时候,其他线程也是同时执行的,怎么只让被调试程序执行呢?通过这个命令就可以实现这个需求。
off 不锁定任何线程,也就是所有线程都执行,这是默认值。
on 只有当前被调试程序会执行。
step 在单步的时候,除了next过一个函数的情况(熟悉情况的人可能知道,这其实是一个设置断点然后continue的行为)以外,只有当前线程会执行。
//显示线程堆栈信息
(gdb) bt
察看所有的调用栈
(gdb) f 3
调用框层次
(gdb) i locals
显示所有当前调用栈的所有变量
转自:http://apps.hi.baidu.com/share/detail/32988725
6.调试宏
这个问题超多。在GDB下,我们无法print宏定义,因为宏是预编译的。但是我们还是有办法来调试宏,这个需要GCC的配合。
在GCC编译程序的时候,加上-ggdb3参数,这样,你就可以调试宏了。
另外,你可以使用下述的GDB的宏调试命令 来查看相关的宏。
info macro – 你可以查看这个宏在哪些文件里被引用了,以及宏定义是什么样的。 macro – 你可以查看宏展开的样子。
7.源文件
这个问题问的也是很多的,太多的朋友都说找不到源文件。在这里我想提醒大家做下面的检查:
编译程序员是否加上了-g参数以包含debug信息。 路径是否设置正确了。使用GDB的directory命令来设置源文件的目录。
下面给一个调试/bin/ls的示例(ubuntu下)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | $ apt-get sourcecoreutils $ sudoapt-get installcoreutils-dbgsym $ gdb /bin/ls GNU gdb (GDB) 7.1-ubuntu (gdb) list main 1192 ls.c: No such fileor directory. inls.c (gdb) directory ~/src/coreutils-7.4/src/ Source directories searched: /home/hchen/src/coreutils-7.4:$cdir:$cwd (gdb) list main 1192 } 1193 } 1194 1195 int 1196 main (int argc, char **argv) 1197 { 1198 int i; 1199 struct pending *thispend; 1200 int n_files; 1201 |
8.条件断点
条件断点是语法是:break [where] if [condition],这种断点真是非常管用。尤其是在一个循环或递归中,或是要监视某个变量。注意,这个设置是在GDB中的,只不过每经过那个断点时GDB会帮你检查一下条件是否满足。
9.命令行参数
有时候,我们需要调试的程序需要有命令行参数,很多朋友都不知道怎么设置调试的程序的命令行参数。其实,有两种方法:
gdb命令行的 –args 参数
gdb环境中 set args命令。
10.gdb的变量
有时候,在调试程序时,我们不单单只是查看运行时的变量,我们还可以直接设置程序中的变量,以模拟一些很难在测试中出现的情况,比较一些出错,或是switch的分支语句。使用set命令可以修改程序中的变量。
另外,你知道gdb中也可以有变量吗?就像shell一样,gdb中的变量以$开头,比如你想打印一个数组中的个个元素,你可以这样:
1 2 3 4 5 | (gdb) set$i = 0 (gdb) p a[$i++] ... #然后就一路回车下去了 |
当然,这里只是给一个示例,表示程序的变量和gdb的变量是可以交互的。
11.x命令
也许,你很喜欢用p命令。所以,当你不知道变量名的时候,你可能会手足无措,因为p命令总是需要一个变量名的。x命令是用来查看内存的,在gdb中 “help x” 你可以查看其帮助。
x/x 以十六进制输出 x/d 以十进制输出 x/c 以单字符输出 x/i 反汇编 – 通常,我们会使用 x/10i $ip-20 来查看当前的汇编($ip是指令寄存器)x/s 以字符串输出 八、command命令
有一些朋友问我如何自动化调试。这里向大家介绍command命令,简单的理解一下,其就是把一组gdb的命令打包,有点像字处理软件的“宏”。下面是一个示例:
1 2 3 4 5 6 7 8 9 10 | (gdb) breakfunc Breakpoint 1 at 0x3475678: filetest.c, line 12. (gdb) command1 Type commands forwhen breakpoint 1 is hit, one per line. End with a line saying just "end". >print arg1 >print arg2 >print arg3 >end (gdb) |
当我们的断点到达时,自动执行command中的三个命令,把func的三个参数值打出来。
(全文完)
http://blog.csdn.net/maintyb011/archive/2010/07/22/5755723.aspx
http://coolshell.cn/articles/3643.html
设置core环境
uname -a 查看机器参数
ulimit -a 查看默认参数
ulimit -c 1024 设置core文件大小为1024
ulimit -c unlimit 设置core文件大小为无限
多线程如果dump,多为段错误,一般都涉及内存非法读写。可以这样处理,使用下面的命令打开系统开关,让其可以在死掉的时候生成
core文件。
ulimit -c unlimited