跳转至

gdb

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]

2 break 断点

2.1 打断点

有 3 种方式设置断点: 1. 根据函数名:b 命名空间::函数名 1. b nm::fun 2. 根据行号:b 【文件名(含路径)】:行号 1. b src/test.cc:30 3. 根据地址:b *地址 1. b *0x67aa88

2.2 删除断点

  1. delete [bp number]: 不指定断点号,将删除所有断点
  2. clear [bp number]: 不指定断点号,将删除所有断点

2.3 从文件中读取断点信息

  1. 将断点信息保存到指定文件:save breakpoints file-name-to-save
  2. 从指定文件读取断点信息:source file-name-to-save

2.4 tbreak

  • 功能:一次性断点,触发一次后就会自动删除
  • 语法:tbreak args

3 调用指定函数

  1. 使用 call 或 print 来执行函数:(gdb) call sleep(1) 或者 (gdb) printf sleep(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

4.1 打印所有线程堆栈

进入 gdb 后,直接执行命令: 1. thread apply all bt :打印所有堆栈 有时堆栈数据很多,直接显示在命令行不方便查看,可以设置输出到文件中,执行如下命令: 2. set logging file mylog.txt :设置日志输出文件 3. set logging on :打开日志 4. thread apply all bt :打印所有堆栈(因为打开了日志,命令执行结果也会输出到日志文件中) 然后如果还想过滤数据,可以使用 grep 命令: 5. grep -B 5 "xxx" mylog.txt

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

6 调试运行中的进程

方法一:gdb attach <pid> 方法二: 1. 在终端输入 gdb 进入 gdb 模式:gdb 2. 连接运行中的进程 (指定进程 id):(gdb) attach processID 3. 使用其它调试指令

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

7.1 显示参数

  • 语法:(gdb) show args

8 调试多进程

8.1 显示进程

  • (gdb) info inferiors:查看所有进程

8.2 切换进程调试

  • (gdb) inferior 进程编号:切换到某一个进程上进行调试

8.3 父子进程调试切换

  • (gdb) set follow-fork-mode parent:调试父进程(默认行为)
  • (gdb) set follow-fork-mode child :当有子进程时,调试子进程

8.4 多进程运行模式

  • (gdb) set detach-on-fork on :调试当前进程时,其它进程也会执行(默认行为)
  • (gdb) set detach-on-fork off:调试当前进程时,其它进程暂停执行

8.5 显示进程调试模式

  • (gdb) show detach-on-fork:显示多进程运行模式
    • on:表示调试当前进程时,其它进程也会执行
    • off:表示调试当前进程时,其它进程暂停执行
  • (gdb) show follow-fork-mode:显示调试父进程还是子进程
    • parent:调试父进程
    • child:调试子进程

9 调试多线程

9.1 显示线程

  1. (gdb) info threads

9.2 切换线程

  1. (gdb) thread id

9.3 单进程下线程运行模式

默认我们调试线程时,所有线程都会执行,如果我们只想要当前线程执行,可以如下设置。 - (gdb) set scheduler-locking [on|off|step] - off:不锁定任何线程,也就是所有线程都执行。 - on:锁定当前线程,使用 next、step 只有当前线程会执行。 - step:在执行 step 的时候,只有当前线程会执行,执行 next 时,所有线程都会执行 (这是默认值)。

9.4 多进程下线程运行模式

  • 当某个程序有多个进程时(inferior),且每一个进程有多个线程时,默认只会执行当前进程下的线程。
  • (gdb) set schedule-multiple on:同时运行所有进程的所有线程(前提 scheduler-locking 是 off 状态,或者 step 时,不是使用 step 命令运行)
  • (gdb) set schedule-multiple off:其它进程的线程不会运行(默认行为)

9.5 查看线程模式

  1. (gdb) show scheduler-locking
  2. (gdb) show schedule-multiple

10 watch 监听变量

  • 功能:当一个监听的变量值发生变化时,程序会在触发变量改变的位置处暂停,方便知道变量值是被那一段代码修改的。

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

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

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.

11 汇编

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+10disassemble $pc - 8

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 指令格式

11.3 显示汇编指令格式

  • 语法:show disassembly-flavor

12 command

  • 功能:为任何断点(或观察点或捕获点)提供一系列命令,以便在程序因该断点而停止时执行。例如,您可能想要打印某些表达式的值,或启用其他断点。

12.1 编写断点的 command 代码

  • 语法:
    1. command 开始,以 end 结束。(command 后不加断点号,默认是最近添加的一个断点)
    2. silent:必须是第一条命令,表示到达断点时不打印断点处的代码信息。
    3. ``
(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

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

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…

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"

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

13 图形化调试界面

  • 方法一:启动 gbd 时指定 -tui 参数,如 gdb -tui program
  • 方法二:进入调试后,按 Crlt + X + A 组合键

13.1 4 种窗口类型

  • src:源码窗口
  • cmd:命令窗口(输入 gdb 指令的窗口,默认只打开这个窗口)
  • asm:汇编代码窗口
  • regs:寄存器窗口

13.2 layout 显示窗口

  • 语法:(gdb) layout name,可使用的 name 如下
    • next:显示下一个窗口
    • prev:显示上一个窗口
    • src:显示源码窗口、命令窗口
    • asm:显示汇编代码窗口、命令窗口
    • split:显示源码窗口、汇编窗口、命令窗口
    • regs
      • 当在 src 布局时,显示寄存器窗口、源码窗口、命令窗口
      • 当在 asm or split 布局时,显示寄存器窗口、汇编窗口、命令窗口

When in src layout display the register, source, and command windows. When in asm or split layout display the register, assembler, and command windows.

13.3 winheight 调整窗口大小

  • 语法:
    • (gdb) winheight win_name + count:窗口增加 count 行
    • (gdb) winheight win_name - count:窗口减少 count 行
    • win_name 有 4 种:srccmdasmregs

13.4 focus 更改当前可滚动的 TUI 窗口

当启动多个窗口时,只能有一个窗口是处于可滚动状态,如果此时需要切换滚动窗口,可使用focus命令进行切换
  • 语法:(gdb) focus name,可使用的 name 如下
    • next:使下一个窗口激活滚动
    • prev:使上一个窗口激活滚动
    • src:使源码窗口激活滚动
    • asm:使汇编代码窗口激活滚动
    • regs:使寄存器窗口激活滚动
    • cmd:使命令窗口激活滚动

13.5 refresh 刷新窗口

当代码中有打印消息到控制台操作时,会污染窗口,此时使用 refresh 可以刷新窗口。 - 语法 (gdb) refresh

14 打印变量值

14.1 打印 STL 数据

14.1.1 安装 Python 插件

  1. Python 插件获取
    1. 从 github 上下载工具 libstdc++-v3/python
    2. 或者从 gcc 源码包根目录下 libstdc++-v3/python(在高版本 gcc 上有)
  2. 编写 ~/.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
  1. 在进入 gdb 模式下,可以使用 p 变量名 来打印 STL 数据

14.2 ptype 打印对象类型

  1. ptype 对象名
  2. 示例
(gdb) ptype mm
type = std::map<unsigned long, std::map<unsigned int, unsigned int>>

15 高级调试技术

15.1 生成 coredump 文件

  1. generate-core-file [file] 或者 gcore [file],如果不指定 file 名,那么默认生成 core.pid 文件。

15.2 调试 coredump 文件

  1. gdb 可执行程序 core文件
  2. btwhere 查看堆栈信息

15.3 修改变量值

  • 语法:
    • set var variable=expr,比如:set var i=1
    • set {type}address=expr,比如:set {int}0x8047a54 = 1,其中 0x8047a54 地址指向的 int 类型
    • 同样也可以修改寄存器的值,比如:set var $eax = 8

15.4 条件断点

  • 语法:break … if cond
  • 示例:break 20 if i==1:在当前文件第 20 行且变量 i==1 时,触发断点

15.5 跳转指定位置

跳转命令不会改变当前堆栈帧、堆栈指针、任何内存位置或除程序计数器之外的任何寄存器的内容。

jump 的简写是 j 跳转到指定行(通常用于错过了调试点,需要回溯)

# 先打断点,再跳转
(gdb) b linenum
(gdb) j linenum

15.6 重新加载程序

  • 使用场景:当修改了源代码,编译生成了新的库或者程序时,重新加载。
  • 方法:
    • 执行 runstart 命令,重新执行,同时会提示重新加载库或程序。
    • 或者使用 file 程序/库 重新加载。

15.7 coredump 文件堆栈信息输出文件

使用以下命令来运行 GDB 并加载 core dump 文件:

gdb your_executable_file core_dump_file

将获取的堆栈信息写入文件。你可以使用 GDB 的输出重定向功能将堆栈信息写入文件,比如:

set logging on  # 开启日志记录
set logging file stack_trace.txt
# 将堆栈信息输出到文件
thread apply all bt full
# 关闭日志记录
set logging off

thread apply all bt full 命令会获取所有线程的完整堆栈信息。然后,通过使用日志记录功能,将堆栈信息写入到指定的文件 stack_trace.txt 中。

15.8 加载符号表文件

1. 用 gdb 查看 coredump 的时候,或者用 gdb 去运行上述被剥离了符号表(symbol)和调试信息(debug symbol)的二进制文件时,gdb会去自动搜索符号表。 
2. gdb 会去查找当前目录、gdb默认的搜索路径 /usr/lib/debug 、 以及 /usr/lib/debug 下的子路径。
3. debug symbol是一种特殊的symbol。
显示调试信息文件查找路径:
(gdb) show debug-file-directory
The directory where separate debug symbols are searched for is "/usr/lib/debug".
加载调试符号文件:
(gdb) symbol-file get_ip.debug
Load new symbol table from "/home/here4/shw/test/get_ip.debug"? (y or n) y
Reading symbols from /home/here4/shw/test/get_ip.debug...done.

简写汇总

  • 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