天天看点

GDB调试从基础到提高(自我总结)

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