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

 


 

 

 

 

 

 

 

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值