gdb使用
文章目录
GDB: The GNU Project Debugger
gdb能够在程序运行时看到程序里面发生了什么,还能看到程序挂掉的时候在干嘛。。。所以说当你在找bug时是很有用的。。。
官方说gdb可以主要做下面四种事情来帮助你找bug:
- 启动你的程序,指定任何可能导致bug的内容1. 使你的程序在特定的条件停下来1. 当你的程序停下后,检查发生了什么1. 在你的程序里改变一些东西,用于去尝试修正一些错误的影响并继续获取其他信息 渣翻译,将就看。。
依照我翻译的意思,大致将主要命令分为四类:
1.下断点
2.给断点设置条件
3.程序断住后查看相关的信息(变量,堆栈,环境变量)
4.改变程序的一些参数,如变量值,执行语句顺序等等
下面依照这四条来理一下相关命令,一些其他的命令在后面单独说。
1.下断点
1). break
最常用的下断点命令,可以指定在某文件某行号设断点,也可以在某函数设断点,指定所在的线程等,当执行到相应地方就能停住
2). watch
当指定变量的值被改变时停住。其实这里的对象应当是表达式,即当指定表达式的值被改变时停住,但我一般都用来看变量了。另外还有被读时改变,被读被写时改变等。
3). catch
这个我没怎么用过,感觉是更高级一点的断点,它可以让程序在发生某些事件时停下,如加载动态库时(然而我用的版本并没有这个选项),系统调用等等。
特殊的停止点
3). 书签
bookmark
保存设定书签时的各种状态,以便回到你设置书签时的状态。当你调试程序不停next时,突然发现已经跑过了,特别是情况复现费时间时,是不是感觉很难受,书签就可以解决这种问题~~~ 可惜的是这个多线程不支持。。。。而且好像要用gdbserver远程调用才行。。。。
4). 检查点
当一个bug复现较难,这个命令就比较有用了,它可以为当前调试的程序创建一个快照,就在你敲checkpoint那一行,创建检查点后你就可以随便调试了,当你想再次回到那个点,只需要恢复这个快照,restart即可。
info checkpoint
checkpoint
restart
以上三条命令分别是查看检查点,设置检查点,恢复检查点,这个跟书签很像,但书签可以多次返回,而checkpoint 无法在同一点连续设置。结果就是用户只能返回一次,而无法随时任意多次的返回检查点。
5). 追踪点
有些时候,程序的运行结果对时间敏感,当你断住程序时,程序的运行结果可能和原先不同。原先我都是用断点的命令列表来获取一些信息,然后continue,以避免断住程序带来的影响。而追踪点就可以很好的完成这项任务。gdb对追踪点的解释是不停止程序运行追踪程序执行。需要远程调用…
trace
action
collect
while-stepping
tstart
tstop
teval
tdump
tfind
tsave
tstatus
passcount
help一下吧
2.给断点设置条件
1).设定条件
当下断点时可以指定一个条件,当条件满足才断住,可以指定多个逻辑表达式。
例如:
break fun_name if foo==0&&foo1==1
这样一来,只有foo为0且foo1为1时才会在函数fun_name处停住了
另一种方法是condition
condition breaknum expr
例如先b fun_name
然后假如这个断点断点号为1
condition 1 foo==0&&foo1==1
可以达到相同效果
2).为断点设置运行命令
command < breakpointnum >
commandline1
commandline2
…
end
可以给断点设置命令,也就是当断住时可以执行你设好的指令。。我有时会在命令里干一些事情然后continue,,,,总之gdb里的命令都可以用
3.程序断住后查看相关的信息(变量,堆栈,环境变量)
看变量,数组,内存,堆栈,环境变量,机器码,寄存器等等,反正很强大。。
还有一个命令display,可以每次停住时自动显示,也可以单步跟踪时自动显示指定的内容~
step 可以一步一步的执行,next同step,但它不会跟进函数
另外还有reverse-next,reverse-step,可以反向走回去。可惜它不支持多线程,而且好像也要远程调用。。。
1).查看数组
p *array@array_len
查看数组名为array长度为array_len的数组内容
2).查看内存
x /2bx 0x400def
/后跟的分别是个数,单位,格式,例如上面就代表16进制显示两个byte,起始地址是0x400def
3).查看汇编代码
disassemble fun_name
还可以指定其他的,help查看
stepi,nexti单步执行汇编指令
4).查看加载的库
info sharedlibrary
4.改变程序的一些参数,如变量值,执行语句顺序等等
1).改变变量的值
p val = xxx
将val的值改为xxx
2).更改程序执行路径
jump < linenum >
跳到指定行执行
这个厉害了,随便你跑,当然还是要小心不要把程序跑死了
还有以偏移量为参数的
jump +1
跳过下一行继续执行
当然有正还有负,但是正比较常用
3).强制函数返回
return
还能指定返回值
4).强制调用函数
call < funname >
此处需注意,若欲调用的函数在当前上下文中未查询到原型,则返回值会被认为是int类型,而返回值被认为是可变参的,若在这种情况下调用malloc函数就会出问题,返回的值是一个整数,而不是void*,此时若强转为void *或别的指针类型,在64位系统上会出问题,因为64位cpu,int类型为4字节,而指针类型为8字节,因此返回出来的值被截断了,再怎么转也是错误的。此时正确的调用方式是把函数指针强转成正确的类型以保证返回值正确,比如想用malloc分配100字节空间,然而使用ptype查看函数原型发现是type = int(),则可断定原型未找到,则如下方式可正确调用:
p ((void *(*)())malloc)(100)
其中void () ()是一个函数指针类型
5.杂项
1).禁止分页
有时候gdb会输出很多信息导致分页,需要你输入return或quit后才能接着显示后面的信息,禁止分页就可以避免这种烦恼了
set pagination off
2).环境变量
set $foo = 0
环境变量有时也很有用,比如有个函数,原型为void fun(int iFoo,FooStruct *stFoo);这个函数iFoo是入参,stFoo是出参,FooStruct是一个结构体类型,你想利用call手动调用这个函数以获取一些信息用来调试,那么此时可以这样:
p $Foo = (FooStruct *)malloc(sizeof(FooStruct))
call fun(num, $Foo)
$Foo就是一个环境变量
3).多线程线程切换
info thread
thread thread-id
上面两条命令分别是查看线程信息,以及切换线程。注意thread-id是info thread时分配的线程号,而不是tid(thread id)。
4).设置执行时调度锁定模式
set scheduler-locking [off on step]
多线程情况下,在使用step或者continue命令调试当前被调试线程的时候,其他线程也是同时执行的,怎么只让被调试程序执行呢?通过这个命令就可以实现这个需求。
on:完全锁定,只有当前线程可以执行
off:不锁定,线程可能在任何时候被抢占(默认,也就是说所有线程都可能运行)
step:step时只有当前线程执行,next时其他线程可能会被执行
5).忽略信号处理
用gdb调试时,有时会经常出现SIGUSR1,然后gdb就停在一些不是你设定断点的地方
查看gdb对SIGUSR1信号的处理情况:
(gdb) info signal SIGUSR1
停用
handle SIGUSR1 noprint nostop
6).切换栈帧
我们都知道,在发生函数调用的时候,调用者的栈里可能存放这着它的局部变量和它调用函数的入参,返回地址等等信息,等将来被调用函数返回时可以继续处理,而gdb可以切换栈帧(或是保存的寄存器之类的?),你可以查看之前的调用者的一些环境。
up down frame
上面这三个命令分别是向上向下移动栈帧及移动到指定栈帧,0是栈顶的那个栈帧,可以通过bt查看
简单整理,仅供参考
7).汇编风格显示与设置
at&t风格汇编: 指令 源操作数 目的操作数
intel 风格汇编:指令 目的操作数 源操作数
show disassembly-flavor
显示风格
set disassembly-flavor [intel | att]
设置风格