0x32c8
0x32cc
0x32d0
0x32d4
0x32d8
0x32dc
0x32e0
End of assembler dump.
http://blog.csdn.net/yangzhongxuan/article/details/6892221
gdb调试(一)启动调试程序
一、启动
>>gdb启动
gdb 调试之前加载调试符号,即编译时候加 –g选项,如 gcc file.c –g –o target
启用gdb的方法种有3种,一种是启动core,还有是attach一个已经运行的进程。
1. gdb
2. gdb
用gdb同时调试一个运行程序和core文件,core是程序非法执行后core dump后产生的文件。
3. gdb
如果你的程序是一个服务程序,那么你可以指定这个服务程序运行时的进程ID。gdb会自动attach上去,并调试他。 program应该在PATH环境变量中搜索得到。
>>运行调试程序
1. run argv1 argv2
gdb带参数运行
2. run
不带参数运行
3. set args argv_a argv_b
gdb启动程序执行后可重新设置参数
4. run > ./output
gdb启动程序时进行重定向
http://sourceware.org/gdb/current/onlinedocs/gdb
二、断点设置
gdb断点分类:
以设置断点的命令分类:
breakpoint
可以根据行号、函数、条件生成断点。
watchpoint
监测变量或者表达式的值发生变化时产生断点。
catchpoint
监测信号的产生。例如c++的throw,或者加载库的时候。
gdb中的变量从1开始标号,不同的断点采用变量标号同一管理,可以 用enable、disable等命令管理,同时支持断点范围的操作,比如有些命令接受断点范围作为参数。
例如:disable 5-8
1、break及break变种详解:
相关命令有break,tbreak,rbreak,hbreak,thbreak,后两种是基于硬件的,先不介绍。
>>break 与 tbeak
break,tbreak可以根据行号、函数、条件生成断点。tbreak设置方法与break相同,只不过tbreak只在断点停一次,过后会自动将断点删除,break需要手动控制断点的删除和使能。
break 可带如下参数:
linenum 本地行号,即list命令可见的行号
filename:linenum 制定个文件的行号
function 函数,可以是自定义函数也可是库函数,如open
filename:function 制定文件中的函数
condtion 条件
*address 地址,可是函数,变量的地址,此地址可以通过info add命令得到。
例如:
break 10
break test.c:10
break main
break test.c:main
break system
break open
如果想在指定的地址设置断点,比如在main函数的地址出设断点。
可用info add main 获得main的地址如0x80484624,然后用break *0x80484624.
条件断点就是在如上述指定断点的同时指定进入断点的条件。
例如:(假如有int 类型变量 index)
break 10 if index == 3
tbreak 12 if index == 5
>>rbreak
rbreak 可以跟一个规则表达式。rbreak + 表达式的用法与grep + 表达式相似。即在所有与表达式匹配的函数入口都设置断点。
rbreak list_* 即在所有以 list_ 为开头字符的函数地方都设置断点。
rbreak ^list_ 功能与上同。
>>查看断点信息
info break [break num ]
info break 可列出所有断点信息,info break 后也可设置要查看的break num如:
info break 1 列出断点号是1的断点信息
Num Type Disp Enb Address What
1 breakpoint keep y
stop only if i==1
breakpoint already hit 1 time
1.1 y 0x080486a2 in void foo
1.2 y 0x080486ca in void foo
2、watch
watch [-l|-location] expr [thread threadnum] [mask maskvalue]
-l 与 mask没有仔细研究,thread threadnum 是在多线程的程序中限定只有被线程号是threadnum的线程修改值后进入断点。
经常用到的如下命令:
watch
为表达式(变量)expr设置一个观察点。变量量表达式值有变化时,马上停住程序。
表达式可以是一个变量
例如:watch value_a
表达式可以是一个地址:
例如:watch *(int *)0x12345678 可以检测4个字节的内存是否变化。
表达式可以是一个复杂的语句表达式:
例如:watch a*b + c/d
watch 在有些操作系统支持硬件观测点,硬件观测点的运行速度比软件观测点的快。如果系统支持硬件观测的话,当设置观测点是会打印如下信息:
Hardware watchpoint num: expr
如果不想用硬件观测点的话可如下设置:
set can-use-hw-watchpoints
watch两个变种 rwatch,awatch,这两个命令只支持硬件观测点如果系统不支持硬件观测点会答应出不支持这两个命令的信息:,
rwatch
当表达式(变量)expr被读时,停住程序。
awatch
当表达式(变量)的值被读或被写时,停住程序。
info watchpoints
列出当前所设置了的所有观察点。
watch 所设置的断点也可以用控制断点的命令来控制。如 disable、enable、delete等。
可以为停止点设定运行命令
commands [bnum]
... command-list ...
end
为断点号bnum指写一个命令列表。当程序被该断点停住时,gdb会依次运行命令列表中的命令。
例如:
break foo if x>0
commands
printf "x is %d/n",x
continue
end
断点设置在函数foo中,断点条件是x>0,如果程序被断住后,也就是,一旦x的值在foo函数中大于0,GDB会自动打印出x的值,并继续运行程序。
注意:watch 设置也是断点,如果调试的时候设置的断点(任何种类的断点)过多的时候,watch断点会被忽略,有时候没有任何提示,
这是我在测试的时候发现的,只有把多余的断点删除后才可用。
在调试的时候通常用catchpoints来捕获事件,如c++的异常等。捕获点的设置通过catch与tcatch两个命令。
tcatch所设置的断点停止一次后自动删除,设置断点的方法与catch相同。
用法:catch event
这些event事件如下:
throw
The throwing of a C++ exception.
catch
The catching of a C++ exception.
exception
exception unhandled
An exception that was raised but is not handled by the program.
assert
Ada 语言 assert断言失败时,断点被踩到。
exec
调用exec时断点被踩到。
syscall
syscall [name | number] ...
通过系统函数的名称和系统号,来设置捕获点,当所设定的系统调用时,断点被踩到。
因为经常在linux用c语言,所以主要用到的event是最后四个,其他的没有仔细研究。
例如:
catch syscall open
catch syscall 5
这两个捕获断点一样。
断点的删除与断点的设置同样的重要。删除断点的命令有两个:
delete
用法:delete [breakpoints num] [range...]
delete可删除单个断点,也可删除一个断点的集合,这个集合用连续的断点号来描述。
例如:
delete 5
delete 1-10
clear
用法:clear
删除所在行的多有断点。
clear location
clear 删除所选定的环境中所有的断点
clear location location描述具体的断点。
例如:
clear list_insert //删除函数的所有断点
clear list.c:list_delet //删除文件:函数的所有断点
clear 12 //删除行号的所有断点
clear list.c:12 //删除文件:行号的所有断点
clear 删除断点是基于行的,不是把所有的断点都删除。
gdb断点(六)condition 与ignore
如果为一个断点设置一个条件,每当程序到达这个断点的时候都会去判断是否为TRUE,
只有条件为TRUE时才会在断点出停下。断点条件的调试方式与断点断言的调试方式逻辑是想反的,assert是当条件为FALSE时停止,
所一如果在条件调中用断言的话应该是:condition !assert(exp)。
断点的条件调试优越性在同个地方设置多个断点的时候体现的更完美。这样我们就可以控制在
同一个断点,因不同的条件(不同的地方调用)而停止程序,同时你也可以自定义命令行,来
打印所需要的信息。
设置断点的条件方式如下:
1、设置断点的时候加入条件
break foo if value_a > value_b
2、用condition命令
condition bnum expression
例如: condition 6 if value_a == 10
如果你设置的断点条件,无效会提示:(这于断点的上下文有关,关于断点的上下文会子专门章节阐述)
No symbol "foo" in current context
3、取消断点条件
condition bnum
4、断点条件特殊用法
断点条件的一个特殊用法是,程序只有在到达断点一定次数之后才会停止。这用一个特殊的命令可以实现。
ignore bnum count
ignore 设置的触发条件在重新加载程序之后自动删除。
ignore 2 10 //触发断点10次后,才会停止,每次触发断点count自减1
如果一个断点及设置了条件,又设置了触发次数,在触发次数count为0之前,是不会判断断点的条件。
ignore 命令对breakpoint watchpoint catchpoint都有效。
gdb断点(七)为断点设定指令集Breakpoint Command Lists
如果想在程序停止在断点的时候,打印信息,或给一些有价值的变量赋值以便定位bug,或者是激活其他断点,可以为这个断点设置一些指令集,完成这些操作。gdb的commands指令帮你实现这个功能能。
用法:
commands [range...]
... command-list ...
end
例1:在设置断点的时候设定命令集
每个指令以行的形式设置,每行输入一个gdb指令,结束的时候一end结束。
break foo if x>0
commands //指令集设置命令
silent //断点触发时不打印断点信息
printf "x is %d\n",x
cont
end //指令集设置结束时必须用end结束
例2:为某个指定的断点设置指令集
commands 403
silent
set x = y + 4
cont
end
三、gdb运行指令
continue [ignore-count]
继续运行程序,直到结束或者触发下个断点。
step
单步执行,可进入函数内部。
step count
next [count]
以文件行为参考,运行下一行指令。遇到函数调用,跳过函数内部。
finish
运行至函数返回。
until
跳出循环。
advance location
运行至指定的地方,如函数,指定行,指定地址。
例如:
advance list_display //运行至list_display 函数出
advance 23 //运行至23行
stepi
单步执行一条机器指令
nexti
运行一条机器指令
什么是函数调用栈
程序每调用一次函数,关于这个函数的信息就会产生。这些信息包括,调用函数的地方、函数的参数、被调用函数变量等。这些信息存储在一个叫做函数调用信息帧的内存中,这些函数信息帧就组成了函数调用栈。
gdb提供了一些指令可以查看这些帧中的信息。当查询函数变量的信息时,gdb就是从这个被选中的帧内获取信息,但是查看被选中帧外的变量信息是非法的。当程序运行停止的时候,gdb会自动选择当前被调用的函数帧,并且打印简单帧信息。
gdb调试(四)函数调用栈之--frame
函数调用栈由连续的栈帧组成。每个栈帧记录一个函数调用的信息,这些信息包括函数参数,函数变量,函数运行地址。
当程序启动后,栈中只有一个帧,这个帧就是main函数的帧。我们把这个帧叫做初始化帧或者叫做最外层帧。每当一
个函数被调用,一个新帧将被建立,每当一个函数返回时,函数帧将被剔除。如果函数是个递归函数,栈中将有很多帧是
记录同一个函数的。但前执行的函数的帧被称作最深帧,这个帧是现存栈中最近被创建的帧。
在程序内部,函数栈帧用函数的地址来标记。一个帧由一定字节的内存组成,每个字节都有自己的地址 。每种类型的计
算机有个约定,用一个特殊字节的地址存放函数帧的地址。通常函数帧的地址存放在一个称作帧指针的寄存器中--$fp.
gdb 为所有存活的栈帧分配一个数字编号,最深帧的编号是0,被它调用的内个帧的编号就是1。这些编号子程序中是不
存在的,只不过时调试的时候被gdb用的。
关于函数帧的两个指令:
frame args
移动到args指定的栈帧中去,并打印选中的栈的信息。args可以时帧编号或者时帧的地址。如果没有args,则打印当前帧的信息。
select-frame args
移动到指定的帧中去,不打印信息。
gdb调试(四)函数调用栈之Backtraces
通过产看栈信息,我们可以了解栈内帧的编号或地址,通过选择帧我们可以移动到指定的帧内去产看信息。
1、查看栈信息
产看函数调用栈的几个函数
bt
显示所有的函数调用栈帧的信息,每个帧一行。
bt n
显示栈定的n个帧信息。
bt -n
显示栈底的n个帧信息。
bt full
显示栈中所有帧的完全信息如:函数参数,本地变量
bt full n
用发同上。
bt full -n
例如:
(gdb) bt
#0 get_net_ipaddr (s=5, ifr=0xbfffe7ec, ipaddr=0xbfffe88c "192.168.1.200") at netif.c:67
#1 0x080491e2 in netinf_get (dev=0x8049a60 "eth0", ipaddr=0xbfffe88c "192.168.1.200", isup=0xbfffe844,
netmask=0xbfffe86c "\325I\024", hwaddr=0xbfffe84c "\001") at netif.c:296
#2 0x08048813 in main () at netif.c:34
(gdb) bt full
#0 get_net_ipaddr (s=5, ifr=0xbfffe7ec, ipaddr=0xbfffe88c "192.168.1.200") at netif.c:67
err = 0
#1 0x080491e2 in netinf_get (dev=0x8049a60 "eth0", ipaddr=0xbfffe88c "192.168.1.200", isup=0xbfffe844,
netmask=0xbfffe86c "\325I\024", hwaddr=0xbfffe84c "\001") at netif.c:296
s = 5
err = 0
ifr = {ifr_ifrn = {ifrn_name = "eth0", '\000'
sa_data = "\000\000\300\250\001\310\000\000\000\000\000\000\000"}, ifru_dstaddr = {sa_family = 2,
sa_data = "\000\000\300\250\001\310\000\000\000\000\000\000\000"}, ifru_broadaddr = {sa_family = 2,
ifru_newname = "\002\000\000\000\300\250\001\310\000\000\000\000\000\000\000",
ifru_data = 0x2 }}
#2 0x08048813 in main () at netif.c:34
err = 1
isup = 0
ipaddr = "192.168.1.200\000\004\b\270\350\377\277\245\324\025\000\060\340\021\000\273\231\004\b"
2、选择帧
只有查看所选择帧内的信息是合法的,如果要查看指定帧内的信息,首先要移动到指定帧。
frame n
f n
通过帧编号来选择帧,帧编号可以通过bt来查看。
f addr
通过帧地址来选择帧,帧编号可以通过bt来查看。
up n
在栈中向上移动n个帧。即向着最外层移动n个帧。
down n
与 up 反方向移动n个帧。
up-silently n
down-silently n
在栈中移动n个帧,但是不打印信息。
3、查看帧内信息
frame
f
打印帧内函数的信息。
info frame
info f
打印帧的信息。
例如:
(gdb) info frame
Stack level 1, frame at 0xbfffe820: //被选帧的地址
eip = 0x80491e2 in netinf_get (netif.c:296); saved eip 0x8048813
called by frame at 0xbfffe8c0, caller of frame at 0xbfffe7b0 //调用帧和被调用帧的地址
source language c.
Arglist at 0xbfffe818, args: dev=0x8049a60 "eth0", ipaddr=0xbfffe88c "192.168.1.200", isup=0xbfffe844,
netmask=0xbfffe86c "\325I\024", hwaddr=0xbfffe84c "\001" //变量地址
Locals at 0xbfffe818, Previous frame's sp is 0xbfffe820
Saved registers:
ebp at 0xbfffe818, eip at 0xbfffe81c //保存的寄存器值地址
info frame addr
info f addr
打印通过addr指定帧的信息。
例如: info f 0x08048813
info args
打印函数变量的值。
info locals
打印本地变量的信息。
info catch
gdb 调试(五)检查文件之编辑源文件。
gdb 调试的时候可以编辑源文件。
edit location
location可以使行号,函数名,文件中指定的位置。
例:
edit 32 编辑当前文件的32行
edit main 编辑当前文件的main函数
edit main.c:init .编辑main.c中的init函数
edit mian.c:32 编辑main.c中的32行
指定编辑器
在sh shell中指定编辑器:
EDITOR=/usr/bin/vi
export EDITOR
gdb ...
在csh shell中指定编辑器:
setenv EDITOR /usr/bin/vi
gdb ...
gdb 调试(五)检查文件之list
gdb根据记录的调试信息知道我们要调试的文件信息。所以通过gdb我们能查看源码。
同时gdb也提供修改源码文件的指令。
1、 查看源码
gdb中通过list命令查看源码。list每次显示的函数可以指定,下文中假定指定显示的行数为10行。
list linenum
以linenum指定的行号为中心,显示10行
list function
以指定的函数为中心,显示10行
list
重复上一次的list指令,也可以直接按回车键,重复上次指令。
set listsize count
设置每次显示的行数。
show listsize
显示已设置的显示行数。
list linespec
设定显示的文件,list指令都是以指定的文件为域来显示源文件,这个指令可以指定要显示的原文件。
例:list list_test.c
以后的list指令都是显示,list_test.c 的源文件。
list first,last
显示指定起始行到结束结束行的源文件。
例:list 10,100
list ,last
显示以指定的last为结束行,显示10行。
例:list ,30 显示21~30行,共显示10行
list first,
以first为第一行,显示10行。
list +
以上次显示的结束行为起始行显示后10行
list –
以上次显示的起始行为结束行,显示前10行
gdb 调试(五)检查文件之搜索和查看汇编指令
1、 源码文件查找
gdb调试可以通过以下三个命令查找源文件。
forward-search regexp
从源文件首行还是搜索 关键字regexp,并将搜索到的行号记录。这个记录可以被下一个gdb命令引用。
例:for void
搜索结果:38 void
接下来运行命令:list 则显示以38行为中心的10行内容。(要显示的行数可以指定)
search regexp
从当前行向下搜索。
reverse-search regexp
从最后一行向上搜索。
2、 查看机器指令(汇编)
disassemble
disassemble /m 十六进制显示汇编指令
disassemble /r
disassemble start,end 指定起始,结束地址
disassemble start,+length 指定起始地址和显示长度
获取地址可如下面的列子:
(gdb) info line main
Line 895 of "builtin.c" starts at pc 0x634c and ends at 0x6350.
(gdb) info line *0x63ff
Line 926 of "builtin.c" starts at pc 0x63e4 and ends at 0x6404.
(gdb) disas 0x634c, 0x6404
Dump of assembler code from 0x32c4 to 0x32e4:
0x32c4
0x32c8
0x32cc
0x32d0
0x32d4
0x32d8
0x32dc
0x32e0
End of assembler dump.