gdb(GNU Symbolic Debugger)是 Linux 下的 调试器,支持包括 c/c++ 在内的多种语言,本博文主要记录整理了 gdb 工具的常见命令,便于在命令行模式下对 c 程序进行调试。(在VSCode里似乎可以通过配置 gdb 进行代码调试,之后应该会研究一下怎么弄(*▽*))
要生成可调试的程序,需要保留符号表,因此在编译时,应使用 gcc -g 命令
$(CC) -g -o $@ $^
对于要调试的程序 ./main,有两种进入调试的方法:
-
- 执行
gdb
命令; - 在 gdb 环境中,执行
file ./main
加载要调试的程序。
- 执行
-
直接执行
gdb ./main
命令
文章目录
- 常见调试命令
- break(b)
- tbreak
- rbreak
- run\(r\)
- start
- continue\(c\)
- next(n)
- step(s)
- until(u)
- until(u) [location]
- set
- print\(p\) [var/expr]
- display
- list(l)
- finish
- return [retval]
- jump(j) [location]
- quit(q)
- clear [location]
- delete(d) [num...]
- disable [num...]
- enable [option] [num...]
- file [filename]
- watch [varname]
- rwatch
- awatch
- search [regexp]
- backtrace
- frame [spec]
- up/down [n]
- info frame [level]
- info args
- info locals
- info(i) break(b)
- catch
- ptype [var]
- disassemble(dis)
- 调试正在执行的程序
- 调试执行异常崩溃的程序
常见调试命令
break(b)
在源代码某行设置普通断点,括号中是命令简写,下同
用法 b [location]
,在 location 处设置断点,location 的值如下:
-
line_num,整数,表示行号,在当前文件的 line_num 行打普通断点
-
filename:linenum,在 filename 文件的 linenum 行打断点
-
+/-offset,在当前程序暂停位置的 ±offset 处打断点
-
function_name,在函数 function_name 的第一行打断点
-
filename:func,在filename文件的 func 函数第一行打断点
-
*Addr,在地址 Addr 处设置断点,注意前面有个*号
b [location] if [cond],每次运行到 location 处时,计算 cond 的值,为 true 则暂停
当执行不带参数的 break 命令时,在当前行施加断点
tbreak
功能、语法与break一样,但只作用一次,作用后清除该断点
rbreak
作用于函数,语法格式:rbreak [regex]
,regex 为正则表达式,只要函数名满足正则表达式 regex,则在其第一行打断点
run(r)
执行程序,自动停止在第一个断点,若无断点,将执行程序直至结束退出
start
启动程序,自动停在 main 函数的第一句
continue(c)
在断点处停止后重新启动执行,直至下一个断点
next(n)
一行一行代码地执行,即单步调试
step(s)
与next类似,但遇到函数会进入其中
until(u)
运行程序直至退出循环体
until(u) [location]
该命令会直接使程序运行到location代码处
set
格式 set $var = val
,设置变量 var 的值为 val,等号可以不写。
set args [arg1 arg2 ...]
设置命令行参数,show args
可查看之。
print(p) [var/expr]
打印指定变量 var 的内容或者表达式 expr 的计算结果,可以打印类和结构体变量的值。当存在多个不同域的同名变量时,通过::指定之,如 file::var、function::var 等。对于指针,可如下操作
-
p *ptr@len
,打印指针 ptr 的内容,长度为 len (Bytes) -
p arr[index]
,查看数组的 index 元素
display
与 print 类似,但每当程序暂停执行,Debugger 就会自动打印变量的值。有两种格式:display [expr]
以及 display/fmt [expr]
。
/fmt 可指定数据显示格式,如十六进制/x,八进制/o,有符号十进制/d,无符号十进制/u,二进制/t,浮点数/f,字符形式/c,注意 display 与 /fmt 中间没有空格。
使用 display 命令查看的目标变量或表达式,都会被记录在一张列表(称为自动显示列表)中。通过执行 info dispaly 命令,可以打印出这张表,其显示格式为 Num Enable Expression。
undisplay [varname/num...]
或 delete display [varname/num...]
可删去自动显示列表中的对应 var/expr
disable display [num...]
可将自动显示变量禁用(自动显示表中对应的变量的 Enable 项将变为 n)
enable display [num...]
可再次启用自动显示(Enable 项将变为 y)
list(l)
显示源程序的代码内容,包括所在行号,每次显示10行,再次执行 l 命令(或直接回车),会自动显示后续内容。
list [loaction]
,可以显示 loaction 前后共10行的内容 (n-5
∼
\sim
∼ n+4)。
finish
正常执行并退出正在执行的函数,并在退出函数后暂停
return [retval]
立即结束当前调用函数并返回指定值,到上层调用处暂停
jump(j) [location]
使程序从当前要执行的代码处,直接跳转到指定位置继续执行
quit(q)
停止调试
clear [location]
清除 location 处的断点,不写 location 时将清除所有断点
delete(d) [num…]
清除编号为 num 的断点,可同时指定多个,不写num时清除所有断点
disable [num…]
禁用断点
enable [option] [num…]
激活断点编号为 num 的断点,option值:
-
once,临时激活,作用一次后再次回到禁用状态
-
count [cnt],cnt为整数,临时激活,作用 cnt 次后回到禁用状态
-
delete,激活断点,作用一次后永久删除(类似 tbreak 创建的断点)
file [filename]
重新指定待调试的程序,实际上是从 filename 文件中获取符号表
watch [varname]
设置“观察断点”,只要某个变量或者表达式的值发生改变,程序就会暂停。
对于这类参数中包含变量名 varname 的命令,都需要先进入调试运行状态( run 或 start),才能获取符号表,进而进行设置。
rwatch
只要出现读取目标变量/表达式的值的操作,就暂停
awatch
只要出现读取目标变量/表达式的值的操作,或者改变其值的操作,就暂停(watch与rwatch的功能综合)
search [regexp]
查看满足正则表达式 regexp 的代码,递增行号序查询,返回第一次出现的代码行内容
backtrace
打印当前调试环境中的所有栈帧信息,格式 backtrace [-full] [n]
,-full 表示同时打印局部变量的值,n 为正整数时,打印最里层的 n 个栈帧信息,为负整数时打印最外层的 n 个栈帧信息。
注意,当调试多线程程序时,该命令仅用于打印当前线程中所有栈帧的信息如果想要打印所有线程的栈帧信息,应执行 thread apply all backtrace
命令
frame [spec]
查看指定栈帧,spec 可以取如下值
-
指定栈帧号 level。0 为当前被调用函数的栈帧号,最大的栈帧对应的通常就是main()
-
指定栈帧地址。栈帧地址可以通过 info frame [level] 命令查看
-
指定函数名。如递归函数对应多个栈帧时,会指定编号最小的栈帧
up/down [n]
指定当前栈帧编号的 +/-n 的编号的栈帧为新的当前栈帧
info frame [level]
查看栈帧信息
info args
查看当前函数的参数值
info locals
查看当前函数的各局部变量的值
info(i) break(b)
查看所有断点
catch
设置“捕捉断点”,监控程序中的某一事件,当目标事件发生时,程序暂停。
命令格式 catch [event],event 类型如下:
-
throw [exception] 当程序抛出异常时,暂停程序执行
-
catch [exception] 当程序捕获异常时,暂停程序执行
-
load [regexp] 加载目标动态库时,暂停程序执行
-
unload [regexp] 卸载目标动态库时,暂停程序执行
ptype [var]
查看 var 的变量类型
disassemble(dis)
查看汇编代码
调试正在执行的程序
首先获取其进程ID,执行 gdb attach [pid]
进入调试 (或者进入 gdb 后执行 attach [pid]
)。
调试完成后,要想让该进程继续执行,需要将 GDB 与程序分离,分以下两步:
- 执行
detach
指令,分离 GDB 与进程; - 执行
quit(q)
,退出GDB调试
若已运行的程序没有符号表,可以从其 debug 版本 file 进符号表,然后再 attach
调试执行异常崩溃的程序
需要用到 Linux 的核心转储(core dump)功能,在程序崩溃时系统可以将发生崩溃时的内存数据、调用堆栈情况等信息自动记录下来,并存储到一个文件中,称为 core 文件。
执行 gdb xxx xxx_core
即可定位崩溃问题所在。
核心转储功能默认是关闭的,通过 ulimit -c
或 ulimit -a
可查看 core file 的大小限制,默认是0,即不产生core file。
ulimit -c unlimited
或者 ulimit -c [size]
让 core file 可以产生,size 的单位是 blocks(512Bytes),这样一旦程序发生崩溃,就会在其文件夹下生成 core 文件。