gdb教程¶
1.1 基本调试指令¶
(gdb)表示已进入调试程序,不代表实际指令,[command]中表示指令的全拼
1. 进入gdb调试:gdb 可执行程序
2. 设置参数【可选】:set args 参数1 参数2 ... 参数n
3. 开始执行程序 (start 将会在程序入口 main 函数打断点,再运行 run 停在 main 函数上):(gdb) start
4. 列出带行号的源代码(运行处上下几行):(gdb) l[list]
5. 打断点:(gdb) b[break] 行号
6. (重新)运行程序到下一个断点处停止:(gdb) r[run]
7. 继续到下一个断点处停下:(gdb) c[continue]
8. 往下执行一行代码(遇到函数不进入函数内部):(gdb) n[next]
9. 往下执行一行代码(遇到函数进入函数内部):(gdb) s[step]
10. 打印变量值:(gdb) p[print] 变量名
11. 终止调试:(gdb) q[quit]
1.2 break断点¶
1.2.1 打断点¶
有3种方式设置断点:
1. 根据函数名:b 命名空间::函数名
1. b nm::fun
2. 根据行号:b 【文件名(含路径)】:行号
1. b src/test.cc:30
3. 根据地址:b *地址
1. b *0x67aa88
1.2.2 删除断点¶
delete [bp number]
:不指定断点号,将删除所有断点clear [bp number]
:不指定断点号,将删除所有断点
1.2.3 从文件中读取断点信息¶
- 将断点信息保存到指定文件:
save breakpoints file-name-to-save
- 从指定文件读取断点信息:
source file-name-to-save
1.2.4 tbreak¶
- 功能:一次性断点,触发一次后就会自动删除
- 语法:
tbreak args
1.3 调用指定函数¶
- 使用call或print来执行函数:
(gdb) call sleep(1)
或者(gdb) printf sleep(1)
1.4 堆栈帧¶
- 解释:GDB 调试器会按照既定规则对它们进行编号:当前正被调用函数对应的栈帧的编号为 0,调用它的函数对应栈帧的编号为 1,以此类推。
- 查看当前堆栈信息:
(gdb) bt
- 跳到堆栈某一帧函数:
(gdb) f[frame] 堆栈帧编号
- 基于当前帧前进一个帧(即帧编号+1):
(gbd) up [number默认1]
- 基于当前帧后退一个帧(即帧编号-1):
(gdb) down [number默认1]
- 查看当前帧存储的信息:
(gdb) info frame
- 查看当前帧函数参数值:
(gdb) info args
- 查看当前帧局部变量值:
(gdb) info locals
1.5 info查看信息¶
info简写是i
1. 打印所有函数名称:(gdb) info functions
2. 打印当前函数局部变量:(gdb) info locals
3. 打印当前函数堆栈帧信息:(gdb) info frame
4. 打印寄存器信息:(gdb) info registers
5. 打印线程信息:(gdb) info threads
6. 打印进程信息:(gdb) info inferiors
1.6 调试运行中的进程¶
- 在终端输入gdb进入gdb模式:
gdb
- 连接运行中的进程(指定进程id):
(gdb) attach processID
- 使用其它调试指令
1.7 设置调试程序的参数¶
有3种方式可以设置程序参数:
1. 启动gdb时:如$ gdb -args ./a.out a b c
2. 进入gdb后:如(gdb) set args a b c
3. run时:如(gdb) run a b c
1.7.1 显示参数¶
- 语法:
(gdb) show args
1.8 调试多进程¶
1.8.1 显示进程¶
(gdb) info inferiors
:查看所有进程
1.8.2 切换进程调试¶
(gdb) inferiors 进程编号
:切换到某一个进程上进行调试
1.8.3 父子进程调试切换¶
(gdb) set follow-fork-mode parent
:调试父进程(默认行为)(gdb) set follow-fork-mode child
:调试子进程
1.8.4 多进程运行模式¶
(gdb) set detach-on-fork on
:调试当前进程时,其它进程也会执行(gdb) set detach-on-fork off
:调试当前进程时,其它进程暂停执行
1.8.5 显示进程调试模式¶
(gdb) show detach-on-fork
:显示多进程运行模式- on:表示调试当前进程时,其它进程也会执行
- off:表示调试当前进程时,其它进程暂停执行
(gdb) show follow-fork-mode
:显示调试父进程还是子进程- parent:调试父进程
- child:调试子进程
1.9 调试多线程¶
1.9.1 显示线程¶
(gdb) info threads
1.9.2 切换线程¶
(gdb) thread id
1.9.3 单进程下线程运行模式¶
默认我们调试线程时,所有线程都会执行,如果我们只想要当前线程执行,可以如下设置。
- (gdb) set scheduler-locking [on|off|step]
- off:不锁定任何线程,也就是所有线程都执行。
- on:锁定当前线程,使用next、step只有当前线程会执行。
- step:在执行 step 的时候,只有当前线程会执行,执行 next 时,所有线程都会执行 (这是默认值)。
1.9.4 多进程下线程运行模式¶
- 当某个程序有多个进程时(inferior),且每一个进程有多个线程时,默认只会执行当前进程下的线程。
(gdb) set schedule-multiple on
:同时运行所有进程的所有线程(前提scheduler-locking 是off状态,或者step时,不是使用step命令运行)(gdb) set schedule-multiple off
:其它进程的线程不会运行(默认行为)
1.9.5 查看线程模式¶
(gdb) show scheduler-locking
(gdb) show schedule-multiple
1.10 watch监听变量¶
- 功能:当一个监听的变量值发生变化时,程序会在触发变量改变的位置处暂停,方便知道变量值是被那一段代码修改的。
1.10.1 添加监听点¶
(gdb) watch variable
:指定监控的变量名(gdb) watch *(data type*)address
:指定监控的变量地址- 示例
(gdb) watch file_size
Hardware watchpoint 3: file_size
(gdb) p &file_size
$1 = (uint64_t *) 0x7fffffffd228
(gdb) watch *(uint64_t *) 0x7fffffffd228
Hardware watchpoint 2: *(uint64_t *) 0x7fffffffd228
1.10.2 显示监听点列表¶
(gdb) info watchpoints
- 示例
(gdb) info watchpoints
Num Type Disp Enb Address What
2 hw watchpoint keep y *(uint64_t *) 0x7fffffffd228
3 hw watchpoint keep y file_size
1.10.3 删除监听点¶
监听点是一种特殊的断点,删除监听点和删除断点的方法一样。
(gdb) d watchpoints
- 示例
(gdb) info b
Num Type Disp Enb Address What
1 breakpoint keep y 0x0000000000432273 in rocksdb::EnvNcdfsTest_Basics_Test::TestBody() at env_ncdfs_gtest.cc:79
breakpoint already hit 1 time
2 hw watchpoint keep y *(uint64_t *) 0x7fffffffd228
3 hw watchpoint keep y file_size
(gdb) d 2
(gdb) d 3
(gdb) info watchpoints
No watchpoints.
1.11 汇编¶
1.11.1 显示汇编代码¶
- 语法:
disassemble position
:只显示汇编代码disassemble -r position
:显示原生指令disassemble -m position
:源码和汇编同时显示(不推荐使用,将来会废弃,存在inline函数显示不全的问题)disassemble -s position
:源码和汇编同时显示(推荐使用)
- 指定的汇编位置position有以下几种格式
function
:如disassemble main
addr-start,addr-end
:如disassemble 0x32c4,0x32d4
addr-start+length
:如disassemble &main+10
、disassemble $pc - 8
1.11.2 切换汇编指令格式¶
当今有2中汇编指令格式:
1. Intel
2. AT&T
示例:Intel和AT&T格式比较
功能:给eax寄存器赋值1
Intel:
add eax, 1
AT&T:
addl $1, %eax
- 语法:
set disassembly-flavor intel
:启用Intel指令格式set disassembly-flavor att
:启用AT&T指令格式
1.11.3 显示汇编指令格式¶
- 语法:
show disassembly-flavor
1.12 command¶
- 功能:为任何断点(或观察点或捕获点)提供一系列命令,以便在程序因该断点而停止时执行。例如,您可能想要打印某些表达式的值,或启用其他断点。
1.12.1 编写断点的command代码¶
- 语法:
- 以
command
开始,以end
结束。(command后不加断点号,默认是最近添加的一个断点) silent
:必须是第一条命令,表示到达断点时不打印断点处的代码信息。- ``
- 以
(gdb) commands [list…]
… command-list …
end
- 示例
(gdb) command
Type commands for breakpoint(s) 1, one per line.
End with a line saying just "end".
>silent
>set variable n = 0
>continue
>end
1.12.1.1 silent示例¶
- 示例:其中第一条时不带silent触发断点,第2条时使用了silent时的执行,2条command都会打印i的值
(gdb)
Breakpoint 3, rocksdb::EnvNcdfsTest_EBasics_Test::TestBody (this=0xf77970) at env_ncdfs_gtest.cc:654
654 std::string dir = v[i];
i = 0
(gdb) n
i = 0
1.12.1.2 输出指令¶
- 官方文档
echo text
:比如echo This is some text\n
printf template, expressions…
:比如printf "foo, bar-foo = 0x%x, 0x%x\n", foo, bar-foo
output expression
eval template, expressions…
1.12.2 显示断点的command代码¶
info break
:查看断点信息,如果有command,就会显示出来- 示例
(gdb) i b
Num Type Disp Enb Address What
1 breakpoint keep y 0x000000000043d48d in rocksdb::EnvNcdfsTest_EBasics_Test::TestBody() at env_ncdfs_gtest.cc:647
breakpoint already hit 1 time
silent
printf "EnvNcdfsTest, EBasics"
1.12.3 删除断点的command代码¶
- 在
command
后立即end
,就会删除端点的command代码 - 示例
(gdb) commands 3
Type commands for breakpoint(s) 3, one per line.
End with a line saying just "end".
>end
1.13 图形化调试界面¶
- 方法一:启动gbd时指定
-tui
参数,如gdb -tui program
- 方法二:进入调试后,按
Crlt + X + A
组合键
1.13.1 4种窗口类型¶
src
:源码窗口cmd
:命令窗口(输入gdb指令的窗口,默认只打开这个窗口)asm
:汇编代码窗口regs
:寄存器窗口
1.13.2 layout显示窗口¶
- 语法:
(gdb) layout name
,可使用的name如下next
:显示下一个窗口prev
:显示上一个窗口src
:显示源码窗口、命令窗口asm
:显示汇编代码窗口、命令窗口split
:显示源码窗口、汇编窗口、命令窗口regs
:- 当在
src
布局时,显示寄存器窗口、源码窗口、命令窗口 - 当在
asm
orsplit
布局时,显示寄存器窗口、汇编窗口、命令窗口
- 当在
When in src
layout display the register, source, and command windows. When in asm
or split
layout display the register, assembler, and command windows.
1.13.3 winheight调整窗口大小¶
- 语法:
(gdb) winheight win_name + count
:窗口增加count行(gdb) winheight win_name - count
:窗口减少count行win_name
有4种:src
、cmd
、asm
和regs
1.13.4 focus更改当前可滚动的 TUI 窗口¶
当启动多个窗口时,只能有一个窗口是处于可滚动状态,如果此时需要切换滚动窗口,可使用focus命令进行切换
- 语法:
(gdb) focus name
,可使用的name如下next
:使下一个窗口激活滚动prev
:使上一个窗口激活滚动src
:使源码窗口激活滚动asm
:使汇编代码窗口激活滚动regs
:使寄存器窗口激活滚动cmd
:使命令窗口激活滚动
1.13.5 refresh刷新窗口¶
当代码中有打印消息到控制台操作时,会污染窗口,此时使用refresh可以刷新窗口。
- 语法(gdb) refresh
1.14 打印变量值¶
1.14.1 打印STL数据¶
1.14.1.1 安装Python插件¶
- Python插件获取
- 从github上下载工具libstdc++-v3/python
- 或者从gcc源码包根目录下libstdc++-v3/python(在高版本gcc上有)
- 编写
~/.gdbinit
文件,其中sys.path.insert
中路径改为插件路径
python
import sys
sys.path.insert(0, '/gcc-12.2.0/libstdc++-v3/python')
from libstdcxx.v6.printers import register_libstdcxx_printers
register_libstdcxx_printers (None)
end
- 在进入gdb模式下,可以使用
p 变量名
来打印STL数据
1.14.2 ptype打印对象类型¶
ptype 对象名
- 示例
(gdb) ptype mm
type = std::map<unsigned long, std::map<unsigned int, unsigned int>>
1.15 高级调试技术¶
1.15.1 调试coredump文件¶
gdb 可执行程序 core文件
bt
或where
查看堆栈信息
1.15.2 修改变量值¶
- 语法:
set var variable=expr
,比如:set var i=1
set {type}address=expr
,比如:set {int}0x8047a54 = 1
,其中0x8047a54地址指向的int类型- 同样也可以修改寄存器的值,比如:
set var $eax = 8
1.15.3 条件断点¶
- 语法:
break … if cond
- 示例:
break 20 if i==1
:在当前文件第20行且变量i==1
时,触发断点
1.15.4 跳转指定位置¶
跳转命令不会改变当前堆栈帧、堆栈指针、任何内存位置或除程序计数器之外的任何寄存器的内容。
jump
的简写是j
跳转到指定行(通常用于错过了调试点,需要回溯)
# 先打断点,再跳转
(gdb) b linenum
(gdb) j linenum
1.15.5 重新加载程序¶
- 使用场景:当修改了源代码,编译生成了新的库或者程序时,重新加载。
- 方法:
- 执行
run
或start
命令,重新执行,同时会提示重新加载库或程序。 - 或者使用
file 程序/库
重新加载。
- 执行
2 简写汇总¶
run
<=>r
:重新运行程序,在第一个断点处暂停。start
:重新运行程序,在程序入口main处暂停step
<=>s
:单步调试,进入函数内部next
<=>n
:单步调试,不进入函数内部continue
<=>c
:继续执行,在下一个断点处暂停jump
<=>j
:跳转代码info
<=>i
:显示信息break
<=>b
:打断点list
<=>l
:显示当前执行位置处上下5行代码frame
<=>f
:切换堆栈delete
<=>d
:删除断点print
<=>p
:打印表达式值until
<=>u
:backtrace
<=>bt
:查看堆栈信息focus
<=>fs
:切换激活窗口的滚动watch
<=>wa
:awatch
<=>aw
rwatch
<=>rw
ignore
<=>ig
stepi
<=>si
tbreak
<=>tb
finish
<=>fin
nexti
<=>ni
winheight
<=>win
:跳转窗口大小directory
<=>dir
disassemble
<=>disas