常用命令
命令(缩写) | 功 能 |
---|---|
run(r) | 启动或者重启一个程序,在第一个断点处暂停。 |
start | 执行至main()起始位置前 |
attach | 调试正在运行的程序或多进程调试。 |
break(b)[linenum] | 打断点 |
list(l)[linenum] | 显示带有行号的源码。 |
continue(c) | 运行至下一个断点。 |
next(n) | 单步(单行)调试程序。 |
nexti | 执行一条机器指令 |
step(s) | 如果有调用函数,进入调用的函数内部;否则,和 next 命令的功能一样。 |
stepi | 执行一条机器指令 |
until(u) until location | 在循环体最后一行使用 until 命令,快速退出循环体。 until n 命令中,n 为行号,该命令会使程序运行至第 n 行代码处停止。 |
finish(fi) | 结束当前正在执行的函数,并在跳出函数后暂停程序的执行。 |
return(return) | 结束当前调用函数(剩余代码不会执行)并返回指定值,到上一层函数调用处停止程序执行。 |
jump(j) | 使程序从当前要执行的代码处,直接跳转到指定位置处继续执行后续的代码(中间代码不执行)。 |
print(p)[val] | 打印指定变量的值。 |
quit(q) | 退出 GDB 调试器。 |
启动GDB
g++ main.cpp -o main.exe -g
-O0~-O4为优化等级,-Og可自动优化
| # 调试正在执行的程序 |
| |
| pidof main.exe # 获取进程号 |
| |
| gdb attach PID # 通过进程号来调试进程 |
| gdb 文件名 PID |
| gdb -p PID |
| |
| # 连接后该程序会暂停 |
| # 结束后令程序继续执行,需手动分离GDB与程序 |
| 1. detach |
| 2. q |
| |
| |
| # 调试执行异常崩溃的程序 |
| # 系统崩溃的内存数据、调用堆栈会存储到core文件,该功能称为核心转储(core dump) |
| # 段错误又称为访问权限冲突,指的是当前程序访问了不可访问的存储空间,比如访问的不存在的空间,又或者是受系统保护的内存空间 |
| |
| ulimit -a # 查看core file size |
| # core file size (blocks, -c) 0 |
| ulimit -c unlimited |
| ulimit -a |
| # core file size (blocks, -c) unlimited |
| |
| gdb main.exe core |
启动GDB调试器常用参数
参 数 | 功 能 |
---|---|
-pid number -p number | 调试进程 ID 为 number 的程序。 |
-symbols file -s file | 仅从指定 file 文件中读取符号表。 |
-q -quiet -silent | 取消启动 GDB 调试器时打印的介绍信息和版权信息 |
-cd directory | 以 directory 作为启动 GDB 调试器的工作目录,而非当前所在目录。 |
–args 参数1 参数2… | 向可执行文件传递执行所需要的参数。 |
启动程序
| run # 执weii行至第一个断点,若无断点则执行完毕 |
| start # 执行至main()起始位置前 |
| # 过程中使用run或start表示重新启动程序 |
向目标程序传递参数
| gdb --args main.exe param # 被argv[]接收 |
| |
| (gdb) |
| set args param |
| run param |
| start param |
| |
| # 将程序结果重定向输出到a.txt |
| run > a.txt |
| file ../main.exe # 指定文件 |
| cd ../ # 修改工作目录 |
| path /demo # 临时修改环境变量,退出gdb失效 |
设置普通断点
| b location [if cond] |
| # b 7 if num>10 # 每次程序执行到第7行都计算num的值,if num>10则在第7行打断点,else 程序继续执行 |
| |
| tbreak # 断点只执行1次,程序暂停后断点消失 |
| rbreak regex # 给所有满足正则表达式的函数开头都打上断点,非临时 |
location 的值 | 含 义 |
---|---|
linenum | linenum 行号。通过 l 命令看代码。 |
filename:linenum | 某文件的某一行 |
+ offset - offset | 当前程序暂停位置向后(+)或向前(-)第几行 |
function | function 函数名,在该函数内部第一行打断点 |
filename:function | 远程文件的函数名 |
| # 条件断点 |
| condition bnum expression |
| condition bnum |
| # 参数 bnum 用于代指目标断点的编号;参数 expression 表示为断点添加或修改的条件表达式。 |
| info break |
| # 通过此命令得到断点编号 |
| |
| # 使某断点失效count次 |
| ignore bnum count |
设置观察断点
| watch cond # 监控【变量或表达式】,只有cond值发生改变,程序暂停 |
| rwatch cond # 只要出现读取cond操作,程序暂停 |
| awatch cond # 只要出现读取cond或更改值的操作,程序暂停 |
| |
| info watchpoints # 查看当前建立的观察点 |
cond为局部变量时,cond失效则watch失效
监控*p是指针p所指的数据,监控p是指针本身有无改变指向
数组中有任一数据改变,都能监测到并停止程序
watch命令分为硬件观察点和软件观察点。软件观察点,即GDB会单步执行程序,每行代码结束都会检测cond;硬件观察点不会影响效率,但限制个数。awatch和rwatch只能设置为硬件观察点。
设置捕捉断点
| catch event # 监控程序中某一事件的发生,例如程序发生某种异常时、某一动态库被加载时等等,一旦目标事件发生,则程序停止执行 |
| tcatch # 前者是永久,后者是只监测一次 |
event 事件 | 含 义 |
---|---|
throw [exception] | 当程序中抛出 exception 指定类型异常时,程序停止执行。如果不指定异常类型(即省略 exception),则表示只要程序发生异常,程序就停止执行。 |
catch [exception] | 当程序中捕获到 exception 异常时,程序停止执行。exception 参数也可以省略,表示无论程序中捕获到哪种异常,程序都暂停执行。 |
load [regexp] unload [regexp] | 其中,regexp 表示目标动态库的名称,load 命令表示当 regexp 动态库加载时程序停止执行;unload 命令表示当 regexp 动态库被卸载时,程序暂停执行。regexp 参数也可以省略,此时只要程序中某一动态库被加载或卸载,程序就会暂停执行。 |
当 catch 命令捕获到指定的 event 事件时,程序暂停执行的位置往往位于某个系统库(例如 libstdc++)中。这种情况下,通过执行 up 命令,即可返回发生 event 事件的源代码处。
catch 无法捕获以交互方式引发的异常。
查看删除禁用断点
| info breakpoint [n] |
| info break [n] |
| info watchpoint [n] |
| clear location # 删除指定位置处的所有断点 |
| delete [breakpoints] [num] # 删除所有断点或指定断点编号 |
| disable [breakpoints] [num...] |
| enable [breakpoints] [num...] |
| enable [breakpoints] once num... # 临时激活以 num... 为编号的多个断点,但断点只能使用 1 次,之后会自动回到禁用状态 |
| enable [breakpoints] count num... # 临时激活以 num... 为编号的多个断点,断点可以使用 count 次,之后进入禁用状态 |
| enable [breakpoints] delete num... # 激活 num.. 为编号的多个断点,但断点只能使用 1 次,之后会被永久删除。 |
调试命令
| next [count] # n,执行count行代码,调用函数只视为单行 |
| step [count] # s,会进入函数,并在第一行暂停 |
| until [location] # u,只有执行至循环体最后一行,才会快速运行完当前循环体然后停止,否则和next一样为单步执行;跟行号则表示执行至指定位置 |
| print [options --] [/fmt] expr |
| # 输出和修改 |
| print num |
| print num=4 |
| |
| print first@len # 输出数组指定区域 |
| 有:int array[5] = {1,2,3,4}; |
| 执行:print array[0]@2 |
| 得:$1 = {1, 2} |
| |
| print file::variable # 支持域运算符 |
| |
| # 执行一次,则每次程序暂停时都会打印该表达式的值 |
| display[/fmt] expr # display与参数间无空格 |
| info display # 所有display过的表达式 |
| |
| undisplay num # 删除表达式 |
| delete display num |
| |
| enable display num # 激活和禁用表达式 |
| disable display num |
options 参数 | 功 能 |
---|---|
-address on|off | 查看某一指针变量的值时,是否同时打印其占用的内存地址,默认值为 on。该选项等同于单独执行 set print address on|off 命令。 |
-array on|off | 是否以便于阅读的格式输出数组中的元素,默认值为 off。该选项等同于单独执行 set printf array on|off 命令。 |
-array-indexes on|off | 对于非字符类型数组,在打印数组中每个元素值的同时,是否同时显示每个元素对应的数组下标,默认值为 off。该选项等同于单独执行 set print array-indexes on|off 命令。 |
-pretty on|off | 以便于阅读的格式打印某个结构体变量的值,默认值为 off。该选项等同于单独执行 set print pretty on|off 命令。 |
/fmt | 功 能 |
---|---|
/x | 以十六进制的形式打印出整数。 |
/d | 以有符号、十进制的形式打印出整数。 |
/u | 以无符号、十进制的形式打印出整数。 |
/o | 以八进制的形式打印出整数。 |
/t | 以二进制的形式打印出整数。 |
/f | 以浮点数的形式打印变量或表达式的值。 |
/c | 以字符形式打印变量或表达式的值。 |
多线程调试
多线程编译命令
g++ main.cpp -o main.exe -g -lpthread
调试命令 | 功 能 |
---|---|
info threads | 查看当前调试环境中包含多少个线程,并打印出各个线程的相关信息,包括线程编号(ID)、线程名称等。 |
thread id | 将线程编号为 id 的线程设置为当前线程。 |
thread apply id… command | id… 表示线程的编号;command 代指 GDB 命令,如 next、continue 等。整个命令的功能是将 command 命令作用于指定编号的线程。当然,如果想将 command 命令作用于所有线程,id… 可以用 all 代替。 |
break location thread id | 在 location 指定的位置建立普通断点,并且该断点仅用于暂停编号为 id 的线程。 |
set scheduler-locking off|on|step | 默认情况下,当程序中某一线程暂停执行时,所有执行的线程都会暂停;同样,当执行 continue 命令时,默认所有暂停的程序都会继续执行。该命令可以打破此默认设置,即只继续执行当前线程,其它线程仍停止执行。 |
默认情况下,无论哪个线程暂停执行,其它线程都会随即暂停;反之,一旦某个线程启动(借助 next、step、continue 命令),其它线程也随即启动。GDB 调试默认的这种调试模式(称为全停止模式 all stop),一定程序上可以帮助我们更好地监控程序中各个线程的执行。
non-stop模式:调试个别线程时,不影响其他线程的执行。
- 保持其它线程继续执行的状态下,单独调试某个线程;
- 在所有线程都暂停执行的状态下,单步调试某个线程;
- 单独执行多个线程等等。
在 all-stop 模式下,continue、next、step 命令的作用对象并不是当前线程,而是所有的线程;但在 non-stop 模式下,continue、next、step 命令只作用于当前线程。在 non-stop 模式下,如果想要 continue 命令作用于所有线程,可以为 continue 命令添加一个 -a 选项,即执行 continue -a 或者 c -a 命令,即可实现令所有线程继续执行的目的。
| set non-stop on|off # on表示启用non-stop模式 |
| show non-stop # 查看是否开启 |
后台异步调试:command&
暂停后台执行线程:interrupt
反向调试
回退
命 令 | 功 能 |
---|---|
(gdb) record (gdb) record btrace | 让程序开始记录反向调试所必要的信息,其中包括保存程序每一步运行的结果等等信息。进行反向调试之前(启动程序之后),需执行此命令,否则是无法进行反向调试的。 |
(gdb) reverse-continue (gdb) rc | 反向运行程序,直到遇到使程序中断的事件,比如断点或者已经退回到 record 命令开启时程序执行到的位置。 |
(gdb) reverse-step | 反向执行一行代码,并在上一行代码的开头处暂停。和 step 命令类似,当反向遇到函数时,该命令会回退到函数内部,并在函数最后一行代码的开头处(通常为 return 0; )暂停执行。 |
(gdb) reverse-next | 反向执行一行代码,并在上一行代码的开头处暂停。和 reverse-step 命令不同,该命令不会进入函数内部,而仅将被调用函数视为一行代码。 |
(gdb) reverse-finish | 当在函数内部进行反向调试时,该命令可以回退到调用当前函数的代码处。 |
(gdb) set exec-direction mode | mode 参数值可以为 forward (默认值)和 reverse:forward 表示 GDB 以正常的方式执行所有命令;reverse 表示 GDB 将反向执行所有命令,由此我们直接只用step、next、continue、finish 命令来反向调试程序。注意,return 命令不能在 reverse 模式中使用。 |
查看栈信息
每个被调用的函数在执行时,都会生成:
- 函数调用发生在程序中的具体位置;
- 调用函数时的参数;
- 函数体内部各局部变量的值等等。
这些信息会集中存储在一块称为“栈帧”的内存空间中。也就是说,程序执行时调用了多少个函数,就会相应产生多少个栈帧,其中每个栈帧自函数调用时生成,函数调用结束后自动销毁。
main() 主函数对应的栈帧,又称为初始帧或者最外层的帧。多调用一个函数,执行过程中就会生成一个新的栈帧。更甚者,如果该函数是一个递归函数,则会生成多个栈帧。每个栈帧用地址来作标识符(非栈帧本身起始地址),GDB用编号来管理。
| frame spec # spec参数指定栈帧。spec 参数的值,常用指定方法: |
- 通过栈帧的编号指定。0 为当前被调用函数对应的栈帧号,最大编号的栈帧对应的函数通常就是 main() 主函数;
- 借助栈帧的地址指定。栈帧地址可以通过 info frame 命令打印出的信息中看到;
- 通过函数的函数名指定。注意,如果是类似递归函数,其对应多个栈帧的话,通过此方法指定的是编号最小的那个栈帧。
| # 对于选定一个栈帧作为当前栈帧,有 |
| up n # n 为整数,默认值为 1。该命令表示在当前栈帧编号(假设为 m)的基础上,选定 m+n 为编号的栈帧作为新的当前栈帧 |
| down n # 选定 m-n 为编号的栈帧 |
| |
| info frame # 打印栈帧信息 |
- 当前栈帧的编号,以及栈帧的地址;
- 当前栈帧对应函数的存储地址,以及该函数被调用时的代码存储的地址
- 当前函数的调用者,对应的栈帧的地址;
- 编写此栈帧所用的编程语言;
- 函数参数的存储地址以及值;
- 函数中局部变量的存储地址;
- 栈帧中存储的寄存器变量,例如指令寄存器(64位环境中用 rip 表示,32为环境中用 eip 表示)、堆栈基指针寄存器(64位环境用 rbp 表示,32位环境用 ebp 表示)等。
| info args # 查看当前函数各个参数的值 |
| info locals # 查看当前函数中各局部变量的值 |
| # 打印当前调试环境中所有栈帧的信息 |
| backtrace [-full] [n] |
- n:一个整数值,当为正整数时,表示打印最里层的 n 个栈帧的信息;n 为负整数时,那么表示打印最外层 n 个栈帧的信息;
- -full:打印栈帧信息的同时,打印出局部变量的值。
编辑和搜索源码
| edit [location] |
| edit [filename :] [location] |
| |
| # gdb默认编辑器为ex,临时指定编辑器为vim,退出shell失效 |
| export EDITOR=/usr/bin/vim |
| list [n] |
| search <regexp> # 从当前行开始向前搜索,regexp为字符串正则表达式 |
| reverse-search <regexp> # 从当前行开始向后搜索 |
help
help 命令/参数
自动补全
<TAB> 补全
<双TAB> 罗列所有同前缀的命令
完成命令后 <双TAB> 罗列所有参数