目录
1 GDB简介
2 使用GDB调试程序前提
3 GDB调试程序三种方式
3.1 gdb filename 调试目标程序
3.2 gdb attach pid 附加进程
3.3 gdb filename corename 调试core文件
3.3.1 自定义生成的core文件名
4 GDB调试策略
4.1 GDB运行选项
4.2 GDB输出格式
4.2.1 p 输出格式 p/x value
4.2.2 x 输出格式 检查内存
4.3 GDB调试指令
4.4 设置GDB调试断点
4.5 GDB调试信号程序
4.6 GDB调试多线程
4.7 GDB调试多进程
4.8 可自定义GDB调试命令
5 GDB中显示程序源码调试
5.1 GDB TUI模式,GDB自带,无需安装
6 知识扩展
GDB(GNU Debugger)是类 Unix(如 Linux)操作系统下的一款开源的 C/C++ 程序调试器。Linux平台下C/C++程序都是使用GDB调试,注:gcc、g++和gdb都需要额外安装.GDB调试器支持C,C ++,Fortran,Java,Chill,汇编语言和Modula-2。GDB提供gdbserver以支持远程调试。
部分应用不能使用断点调试,因为中断调试影响程序结果,只能使用跟踪点技术,但跟踪点只适用远端目标机。
GDB调试器提供覆盖技术支持程序太大无法载入目标系统内存的应用。
编译时使用-g选项使编译后的可执行文件保留调试符号信息。调试符号信息包括但不局限与可以调试代码、调用堆栈、变量名和函数名。但调试信息不包含代码,若需要在运行环境看到代码,需要连同代码打包。
可以使用strip命令移除程序中调试信息,不带调试信息可以减少程序体积和提高程序执行效率。
调试程序,加上-g选项并建议关闭编译器优化。O0到O4,默认O0不优化;符号文件显示的调试变量等能与源代码对应起来。
Debug、Release模式和GCC选项没有严格对应关系。一般情况下,Debug模式使用GCC的-g -O0开启调试和不优化选项,Release模式使用GCC的-O2 -s -DNDEBUG开启优化和移除调试信息选项。
GCC默认情况下使用-O0不优化作为默认值,-O等于-O1,-Os打开除增加代码大小优化外所有O2的优化。
gdb filename 调试程序,使用run(r)命令启动程序;
gdb attach pid将GDB调试器附加到正在运行的进程。调试器暂停下来,可以添加断点或continue;调试结束后,detach使程序和调试器分离,continue继续,退出GDB即可。
gdb program 1234或gdb -p 1234
Linux默认不开启生成core文件,使用ulimit -c查看。使用ulimit -c unlimited开启,但只能当前会话有效。希望永久有效,将ulimit -c unlimited加入到/etc/profile文件最后一行,再让配置文件生效。默认生成的core文件名为core.
默认core文件名core,但可通过修改/proc/sys/kernel/core_uses_pid文件内容控制是否添加pid作为扩展,内容1表示扩展.
/proc/sys/kernel/core_pattern文件可控制core文件生成位置和文件名。
通过echo "/corefile/core-%e-%p-%t" > /proc/sys/kernel/core_pattern 修改文件内容。生成的core文件将存储在/corefile目录下。
注:用户必须对core文件目录具有写权限,否则因权限不足无法生成core文件.
以下为支持的参数配置:
参数名称 | 参数含义(中文) |
---|---|
%p | 添加 pid 到 core 文件名中。主进程号,即包含main函数进程 |
%t | 添加 core 文件生成时间(UNIX)到 core 文件名中 |
%e | 添加程序名到 core 文件名中 |
%u | 添加当前 uid 到 core 文件名中 |
%g | 添加当前 gid 到 core 文件名中 |
%s | 添加导致产生 core 的信号到 core 文件名中 |
%h | 添加主机名到 core 文件名中 |
GDB运行选项 | 含义 |
gdb --help | GDB帮助文档 |
gdb -silent | GDB不打印版本和介绍信息 |
gdb -d directory | 指定gdb的源码搜索路径 |
path directory | 将directory添加到path.path值存储可执行文件搜索路径列表 |
show paths | 显示path值.path值存储可执行文件搜索路径列表 |
cd directory | 设置GDB工作目录 |
pwd | 输出GDB工作目录 |
run > outfile | 改变程序输出位置。默认输出到终端,info terminal可查看 |
show directories | 显示源码查询路径。源码路径默认包含cdir、cwd。可通过directory dirname或dir dirname加入path到搜索路径 |
signal signal | 向程序发送信号signal |
TAB | 补全命令 |
使用gdb attach时,查找可执行程序和源代码先后顺序:1)当前工作目录,2)源文件搜索目录。gdb attach时,运行run,将杀死进程;不执行detach,直接quit,默认detach,不会杀死进程。
命令 | 含义 |
x | 以十六进制输出 |
d | unsigned int 输出 |
o | 八进制输出 |
t | 二进制输出 |
a | 作为地址输出 |
c | 作为字符输出 |
f | 浮点型输出 |
注:动态数组输出方式,int *array = (int *) malloc (len * sizeof (int)); p *array@len;vector直接p v
命令 | 含义 |
x addr | 输出addr内存 |
x/nfu addr | n重复几次,f显示格式,u字节单元.b bytes,h two bytes,w four bytes.g eight bytes. |
命令名称 | 命令缩写 | 命令说明 |
---|---|---|
run | r | 运行一个程序 |
continue | c | 让暂停的程序继续运行 |
next | n | 单步运行,遇到函数直接跳过 |
step | s | 单步步入遇到函数进入 |
until | u | 运行到指定行停下来.u 84 |
finish | fi | 结束当前调用函数,到上一层函数调用处.执行整个函数 |
return | return | 结束当前调用函数并返回指定值,到上一层函数调用处.立即结束,后面代码不执行 |
p | 打印变量或寄存器值。p &server.port 来输出 server.port 的地址值;p this 显示当前对象的地址,p *this 来列出当前对象的各个成员变量值;p func() 命令输出该变量的执行结果,可以使用 p strerror(errno) 将这个错误码对应的文字信息打印出来;print也可修改变量值p server.port=6060,修改之后的值对程序后续运行产生影响 print打印较长变量,例如字符串和数组,输出不全。使用set print element 0设置,可全部输出 |
|
backtrace | bt | 查看当前线程的调用堆栈 |
frame | f | 切换到当前调用线程的指定堆栈,具体堆栈通过堆栈序号指定 |
thread | thread | 切换到指定线程 |
break | b | 添加断点 |
watch | watch | 监视某一个变量或内存地址的值是否发生变化.监视值变化时,断点命中.监视变量失效后,观察点也失效 |
list | l | 显示源码.list + 行数,list - 行数.用于向前向后查看源码 list function:以function为中心打印源码 list first,last:打印first到last行 list filename:number:打印文件中number行前后的源码 list number:打印文件number前后的源码 |
info | info | 查看断点 / 线程等信息 info program:可查看进程状态、停止位置及原因; info line linespec:显示程序映射的地址; info address symbol:依据符号名显示地址; info symbol addr:依据地址显示符号名 |
ptype | ptype | 查看变量类型,也可查看复合数据类型的成员名 |
disassemble | dis | 查看汇编代码.set disassembly-flavor intel设置汇编显示格式 |
jump | j | 将当前程序执行流跳转到指定行或地址.慎用 |
tbreak | tb | 添加临时断点.触发后删除 |
delete | del | 删除断点.如果后面不跟随断点编号,表示删除所有断点.delete 3 5 |
enable | enable | 启用某个断点.如果后面不跟随断点编号,表示启动所有断点 |
disable | disable | 禁用某个断点.如果后面不跟随断点编号,表示禁用所有断点 |
display | display监视变量和内存,每次中断触发自动输出变量或内存的值。info display查看当前自动添加哪些值,使用delete display清除自动输出的变量。使用delete display编号删除某个自动输出的变量 | |
set args | 设置程序启动命令行参数.set args 1 2,多个命令行参数,空格.若参数包含空格,使用引号包裹 使用set args不加参数清除已设置的参数 |
|
show args | 查看设置的命令行参数 |
info thread查看程序所有进程/线程.星号*表示GDB作用于线程.可通过bt查看调用堆栈看是否存在main判断主进程.bt查看当前线程调用堆栈.
info args查看当前函数的参数值.指针类型参数,输出变量地址,使用*解引用输出对象值
help info 查看info帮助手册
因代码中经常使用条件编译方式,生成的可执行程序不一定包含条件代码,导致源码与gdb调试代码不匹配。所以建议以gdb list命令的代码行号为准。
可以使用make命令对makefile文件进行编译:make CFLAGS="-g -O0" -j 4.
gcc编译器,使用CFLAGS选项;g++编译器,使用CXXFLAGS选项。-j表示使用4个进程同时编译,加快编译速度.
whatis expr:显示expr的数据类型
1.break functionname:在函数名上加断点;
2.break lineNo:在行号上加断点;
3.break filename:lineNo:在文件的lineNo行号上加断点;
4.break filename:functionname:在文件的functionname上加断点;
5.break lineNo if condition:条件断点
6.condition 断点编号 condition:普通断点加条件
7.break *address:在没有调试信息或源码,只有地址时很有用
8.break linespec thread threadno 指定线程上设置断点
9.break linespec thread threadno if ... 可以在指定线程上设置断点
GDB显示字符串或字符数组时,如果数值连续重复n次,GDB将以''{0 \
常用函数调用方式_cdecl(c语言默认)和_stdcall,c++非静态成员函数的调用方式_thiscall.
catchpoint 断点用于c++抛出异常和加载库,catch xxx.catchpoint只能捕获指定的事件。可通过help catch查看
watchpoint:只要表达式的值发送变化就停止调试。观察点的实现有可能以软件或硬件实现,取决于系统。系统尽可能使用硬件断点,无法设置硬件观察点,才会设置软件观察点
默认情况下,调试程序信号触发时,信号会被GDB接收.可通过以下方式解决
1.GDB手动使用signal函数发送信息.signal SIGINT
2.GDB命令中输入handle SIGINT nostop print改变GDB信号处理设置,通知GDB收到SIGINT不停止并将信号传递给目标程序
注:存在断点失效情况,换种断点设置方式
多线程下调试会出现运行过程错乱,这是由于线程切换导致.可以使用set scheduler-locking on 开启锁定当前线程,禁止线程切换;set sheduler-locking off关闭选项.
GDB调试器默认情况下调试父进程
1.调试父进程,等子进程fork出来后,使用gdb attach到子进程中,但需要开启一个session窗口调试。
2.进程fork出子进程时,通过set follow-fork mode设置调试父进程还是子进程.
set follow-fork child调试子进程,set follow-fork parent调试父进程.show follow-fork mode查看当前值.
1.gdbtui -q 可执行文件名
2.进入gdb调试页面,快捷键Ctrl+X+A显示GDB TUI
GDB TUI模式提供多个窗口模式,command(cmd),source(src),assembly(asm),register(reg).
使用layout+窗口类型切换窗口,例如layout asm,可以使用help layout查看手册;help winheight查看修改窗口大小手册;当GDB TUI 窗口放大或缩小后,内容不会刷新适应新窗口,通过space 空格键强行刷新;通过focus next切换焦点,help focus查看手册.
《Debugging with GDB:The The gnu Source-Level Debugger》 书籍介绍了GDB 调试知识,pdf版本已保存在百度云盘中.