文章目录
一、gdb简介
GDB是一个由GNU开源组织发布的、UNIX/LINUX操作系统下的、基于命令行的、功能强大的程序调试工具。 对于一名Linux下工作的c/c++程序员,gdb是必不可少的工具;
1、暂停 / 恢复程
程序运行当进程被gdb停住时,你可以使用info program 来查看程序的是否在运行,进程号,被暂停的原因。
在gdb中,我们可以有以下几种暂停方式:
断点(BreakPoint)、
观察点(WatchPoint)、
捕捉点(CatchPoint)、
信号(Signals)、
线程停止(Thread Stops),
如果要恢复程序运行,可以使用c或是continue命令。
3、线程(Thread Stops)
如果程序是多线程,可以定义断点是否在所有的线程上,或是在某个特定的线程。
break thread
break thread if ...
linespec 指定了断点设置在的源程序的行号。threadno指定了线程的ID,注意,这个ID是GDB分配的,可以通过“info threads”命令来查看正在运行程序中的线程信息。如果不指定thread 则表示断点设在所有线程上面。还可以为某线程指定断点条件。如:
(gdb) break frik.c:13 thread 28 if bartab > lim
当你的程序被GDB停住时,所有的运行线程都会被停住。这方便查看运行程序的总体情况。而在你恢复程序运行时,所有的线程也会被恢复运行。
二、gdb使用流程
这里用c程序做基本演示,c++程序也是一样的;
1、使用 gdb 进行调试:
1、调试程序,指定想要调试的程序,这将告诉 gdb 装入名为 fname 的可执行文件。
1、调试开始时,必须先载入要进行调试的程序,可以用以下两种方式:
1、* 在gdb启动时载入程序:
gdb 可执行文件路径
2、* 在启动gdb后载入程序:
file 可执行文件路径
2、调试正在运行程序,用gdb与一个正在运行的程序相连。
两种方法:
1、在UNIX下用ps查看正在运行的程序的PID(进程ID),然后用
gdb PID 格式挂接正在运行的程序。
2、先用gdb 关联上源代码,并进行gdb,
在gdb中用attach命令来挂接进程的PID。
并用detach来取消挂接的进程。
3、调试core文件,可以用 gdb 去检查一个因程序异常终止而产生的 core 文件,
1、Core Dump: Core的意思是内存,Dump的意思是扔出来,堆出来。开发和使用Unix程序时,有时程序莫名其妙的down了,却没有任何的提示(有时候会提示core dumped),这时候可以查看一下有没有形如core.进程号的文件生成,这个文件便是操作系统把程序down掉时的内存内容扔出来生成的, 它可以做为调试程序的参考。
2、生成Core文件
一般默认情况下,core file的大小被设置为了0,这样系统就不dump出core file了。修改后才能生成core文件。
1、#设置core大小为无限
ulimit -c unlimited
2、#设置文件大小为无限
ulimit unlimited
3、这些需要有root权限,在ubuntu下每次重新打开中断都需要重新输入上面的第一条命令,来设置core大小为无限。
4、core文件生成路径:输入可执行文件运行命令的同一路径下。若系统生成的core文件不带其他任何扩展名称,则全部命名为core。新的core文件生成将覆盖原来的core文件。
1)/proc/sys/kernel/core_uses_pid 可以控制core文件的文件名中是否添加pid作为扩展。文件内容为1,表示添加pid作为扩展名,生成的core文件格式为core.xxxx;为0则表示生成的core文件统一命名为core。
可通过以下命令修改此文件:
echo "1" > /proc/sys/kernel/core_uses_pid
2)proc/sys/kernel/core_pattern可以控制core文件保存位置和文件名格式。
可通过以下命令修改此文件:
echo "/corefile/core-%e-%p-%t" > core_pattern,可以将core文件统一生成到/corefile目录下,产生的文件名为core-命令名-pid-时间戳
以下是参数列表:
%p - insert pid into filename 添加pid
%u - insert current uid into filename 添加当前uid
%g - insert current gid into filename 添加当前gid
%s - insert signal that caused the coredump into the filename 添加导致产生core的信号
%t - insert UNIX time that the coredump occurred into filename 添加core文件生成时的unix时间
%h - insert hostname where the coredump happened into filename 添加主机名
%e - insert coredumping executable name into filename 添加命令名
3、用gdb查看core文件
发生core dump之后,用gdb进行查看core文件的内容,以定位文件中引发core dump的行。
第一个参数是程序可执行文件。第二个文件是生成得core文件。
gdb [exec file] [core file]
如:gdb ./test core.xxxxx
或 gdb ./a.out core-file core.xxxx
gdb后,用 bt 命令 backtrace 或 where 查看程序运行到哪里,来定位core dump的文件->行。
待调试的可执行文件,在编译的时候需要加-g,core文件才能正常显示出错信息。
1)gdb -core=core.xxxx
file ./a.out
bt
2)gdb -c core.xxxx
file ./a.out
bt
4、用gdb实时观察某进程crash信息
启动进程
gdb -p PID
c
运行进程至crash
gdb会显示crash信息
bt
5、gdb dump 进程内存
1、获取目标进程的虚拟地址映射
$ cat /proc/[pid]/maps
root@x86_64: # cat /proc/2436/maps |grep vdso
7fffd91f3000-7fffd91f5000 r-xp 00000000 00:00 0 [vdso]
2、dump内存地址
#### commands: dump memory file start-addr end-addr
(gdb) dump memory /home/linux/dump 0x7fffd91f3000 0x7fffd91f5000
3、查看二进制文件
使用vi打开二进制文件分析
vi dump
:%!xxd
使用hexdump查看二进制文件
hexdump -C dump
3、调试符号加载:
远程调试时gdb server和gdb client不在同一个进程中或者相同的机器上,程序在运行时加载到随机地址。加载符号表有两种方式:file 和 add-symbol-file。
1、file /path/to/program :读取程序和符号表到内存中,它本身可以选择被调试的程序,附带的将符号表加载到gdb中,但是它并不能指定地址。
2、add-symbol-file /path/to/symbol-file start-addr :将符号表加载到某个位置。
用法: add-symbol-file FILE [-readnow | -readnever] [-o OFF] [ADDR] [-s SECT-NAME SECT-ADDR]…
含义: 从文件中加载符号表,假设文件已经动态加载。
选项:
FILE:编译好的带调试信息的debug文件。
ADDR:文件文本的起始地址。
每个’-s’参数提供了一个段名和地址,如果数据段和bss段不是与文本连续的,则应该指定。SECT-NAME是在SECT-ADDR中加载的section名。
OFF:一个可选的偏移量,它被添加到没有指定其他地址的所有节的默认加载地址中。
'-readnow’选项将导致GDB立即读取整个符号文件。这使得指挥速度变慢,但可能使未来的行动更快。
2、启动进入gdb
用gdb调试程序,必须在编译时加上-g选项,-g选项的作用是在可执行文件中加入源文件信息。
1、编译一个测试程序,-g表示可以调试,命令如下:
gcc -g test.c -o test
2、启动进入gdb,命令如下:
gdb test
gdb -q test //表示不打印gdb版本信息,界面较为干净;
到此进入gdb启动完成!
3、判断一个文件有没有调试信息:
1、gdb 可执行文件 有会进入gdb,没有会显示没有调试符号。
2、readelf -S main可执行文件|grep debug
如果有debug说明有调试功能,如果没有debug。说明没有带有调试功能,则不能被调试。
三、gdb基本使用命令
1、运行命令:
run (简写 r) 从头运行程序直到遇到结束或者遇到断点等待下一个命令;
r > outfile 使用重定向控制程序输出。
tty /dev/ttyb tty命令可以指写输入输出的终端设备。
cont/continue (简写c ) 继续执行,到下一个断点处(或运行结束)
cont n 跳过n次断点,继续运行。
next (简写 n) 单步执行,跳过不进入函数,把函数当作一条语句执行
next n 执行n条语句
nexti 单步执行语句,但和next不同的是,它会跟踪到子程序的内部,但不打印出子程序内部的语句。
nexti n 执行n条语句
step (简写s) 单行执行,进入函数,一行一行执行,会跟踪到子程序的内部,而且会显示子程序内部的执行情况。
stepi 与step类似,但是比step更详细,是nexti和step的结合。
until 当你厌倦了在一个循环体内单步跟踪时,这个命令可以运行程序直到退出循环体。
until 行号 运行至某行,不仅仅用来跳出循环
finish 运行程序,直到当前函数完成返回,并打印函数返回时的堆栈地址和返回值及参数值等信息。
call 函数(参数) 调用程序中可见的函数,并传递“参数”,如:call gdb_test(55)
jump 行号 让程序跳到指定行开始调试。使用格式:
kill 结束当前程序的调试。
quit (简记 q) 退出gdb
2、设置断点:
break (简写 b) 设置断点。
b n 在第n行处设置断点,(可以带上代码路径和代码名称: b OAGUPDATE.cpp:578)
b fn1 if a>b 条件断点设置
b 行号 if 条件 用于设置条件断点,在循环中使用非常方便,break 10 if i==3
b func 在函数func()的入口处设置断点,如:break cb_button
b filename 行号 用于在指定文件对应行设置断点,break main.c:10
b filename 函数名 用于在指定文件对应函数入口处设置断点,break main.c:main
tbreak 行号 设置临时断点,在设置之后只起作用一次。
delete 断点号n 删除第n个断点
disable 断点号n 暂停第n个断点
enable 断点号n 开启第n个断点
ignore 断点号 忽略次数 忽略断点
condition 断点号 条件表达式 设置断点在一定条件下才能生效。
clear 行号n 清除第n行的断点
delete breakpoints 清除所有断点
info b(info breakpoints) 显示当前程序的断点设置情况
Num: 断点编号
Disp: 断点执行一次之后是否有效: keep:有效 dis:无效
Enb: 当前断点是否有效 y:有效 n:无效
Address:内存地址
What: 位置
commands 设置在遇到断点后执行特定的指令。
注意,commands后面跟的是断点号,而不是断点所在的行号。
在输入命令后,就可以输入遇到断点后要执行的命令,每行一条命令,在输入最后一条命令后输入end就可以结束输入。
commands xxx 设置遇到最后一个遇到的断点时要执行的命令
commands n 设置遇到断点号n时要执行的命令
3、查看源码命令:
list (简写 l) 查看源程序代码,默认显示10行,按回车键继续看余下的。
l 行号 将显示当前文件以“行号”为中心的前后10行代码
l 函数名 将显示“函数名”所在函数的源代码,如:list main
l 不带参数,将接着上一次 list 命令的,输出下边的内容。
forward 字符串 从当前行向后查找匹配某个字符串的程序行。
查找到的行号将保存在$_变量中,可以用 p $_ 命令来查看。
search 字符串 从当前行向后查找匹配某个字符串的程序行。
查找到的行号将保存在$_变量中,可以用 p 命 令来查看。 r e v e r s e − s e a r c h 字符串从当前行向前查找匹配某个字符串的程序行。查找到的行号将保存在 _ 命令来查看。 reverse-search 字符串 从当前行向前查找匹配某个字符串的程序行。 查找到的行号将保存在 命令来查看。reverse−search字符串从当前行向前查找匹配某个字符串的程序行。查找到的行号将保存在变量中,可以用 p $ 命令来查看。
4、查看并打印表达式或变量,函数
print (简写 p)
p 变量/表达式 打印变量或表达式的值。其中“表达式”可以是任何当前正在被测试程序的有效表达式,包括数字,变量甚至是函数调用。
$ 表示给定序号的前一个序号,$$表示给定序号的前两个序号。
如果 $ 和 $$ 后面不带数字,则给定序号为当前序号。
p /格式 变量名 在使用 p 命令时,可以对变量按指定格式进行输出。
其中常用的变量格式:
x:十六进制;
d:十进制;
u:无符号数;
o:八进制;
c:字符格式;
f:浮点数。
p $寄存器 显示寄存器内容
p /x $寄存器 十六进制显示寄存器内容。
p a 将显示整数 a 的值
p ++a 将把 a 中的值加1,并显示出来
p name 将显示字符串 name 的值
p gdb_test(22) 将以整数22作为参数调用 gdb_test() 函数
p gdb_test(a) 将以变量 a 作为参数调用 gdb_test() 函数
x/格式 地址 可以显示地址内容,
x $pc 显示程序指针内容
x/i $pc 显示程序指针汇编。
x/10i $pc 显示程序指针之后10条指令。
x/128wx 0xfc207000 从0xfc20700开始以16进制打印128个word。
x/28hx --- 段错误调试,core文件样例
dir dirname … 以十六进制输出内存块数据
info function 查询函数
info locals 显示当前堆栈页的所有变量
examine 地址 该命令用来查看内存中地址的值,examine 0xxxx
disassemble 函数 该命令用于反汇编,它可以用来查看当前执行时源代码的机器码,disassemble func
info reg 可以显示寄存器内容。
5、查看堆栈信息
where/bt 当前运行的堆栈列表;
bt/backtrace 显示当前调用堆栈指针
backtrace 栈帧个数 打印指定个数的栈帧(stack frame)。
frame 栈帧号 打印指定栈帧。
info stack 查看堆栈使用情况
info frame 显示当前栈帧的详细信息。
select-frame 栈帧号 选择栈帧,选择后可以用info frame来显示栈帧信息。
up/down 改变堆栈显示的深度,跳到上一层/下一层函数
6、设置变量,参数,表达式:
调试运行环境相关命令:
set args 参数/ 设置程序中变量的值。
set 变量=表达式
set 变量:=表达式
display 表达式 在单步运行时将非常有用,使用display命令设置一个表达式后,它将在每次单步进行指令后,紧接着输出被设置的表达式及值。如: display a
info display 显示当前所有的要显示值的表达式。
delete display/undisplay 表达式编号 删除要显示值的表达式。
disable display 表达式编号 暂时不显示一个要表达式的值。
enable display 表达式编号 开启表达式恢复显示
awatch 变量/表达式 用来增加一个观察点(add watch),当表达式的值发生改变或表达式的值被读取时,程序就会停止运行。运行run之后才能设置。
watch 变量/表达式 设置一个观察点,一旦被监视的“表达式”的值改变,gdb将强行终止正在被调试的程序。运行run之后才能设置。如: watch a
whatis 变量/表达式 显示某个变量或表达式的数据类型。
ptype 变量/表达式 和whatis类似,用于显示数据类型,但是它还可以显示typedef定义的类型等。
catch 设置捕捉点来补捉程序运行时的一些事件。如:载入共享库(动态链接库)或是C++的异常
tcatch 只设置一次捕捉点,当程序停住以后,应点被自动删除
ulimit 查看一下系统是否配置支持了dump core的功能。
ulimit -c 或 ulimit -a 可以查看core file大小的配置情况,如果为0,则表示系统关闭了dump core;
ulimit -c unlimited 来打开 dump core。若发生了段错误,但没有 core dump,是由于系统禁止core文件的生成。
info terminal 显示你程序用到的终端的模式。
show args 查看设置好的参数
info program 来查看程序的是否在运行,进程号,被暂停的原因。
gdb attatch pid 加载运行中的进程进行调试
7、分割窗口
layout 用于分割窗口,可以一边查看代码,一边测试:
layout src 显示源代码窗口
layout asm 显示反汇编窗口
layout regs 显示源代码/反汇编和CPU寄存器窗口
layout split 显示源代码和反汇编窗口
Ctrl + L 刷新窗口
8、cgdb强大工具
cgdb主要功能是在调试时进行代码的同步显示,这无疑增加了调试的方便性,提高了调试效率。界面类似vi,符合unix/linux下开发人员习惯;如果熟悉gdb和vi,几乎可以立即使用cgdb。
四、总结
总的来说在Linux下开发程序gdb/cgdb是必须学会使用的,他的强大之处远不止于此,在程序的调试中用它会提高的我们的调试效率,当然gdb的功能与使用技巧还不止于此,多多探索,多多学习使用。