关于GDB调试工具的学习笔记,学会了纯命令行的调试操作,才能更好地理解例如VSCode提供的图形界面的调试功能等,GDB在WSL2 Ubuntu上进行操作。

调试信息

可执行程序可以被调试的前提就是编译时加上了调试信息,通常是debug模式下,加上-g选项。

如果强行调试一个没有调试信息的可执行程序,将不能看见程序中的函数名或变量名等,而只是内存地址,并且会显示一条警告

1
(No debugging symbols found in <program>)

加载程序

正常情况下可以通过调试器来加载程序

1
gdb <program>

也可以先启动gdb,然后加载可执行文件

1
2
gdb
(gdb) file <program>

加载的程序并不会直接执行,需要使用startrun命令来启动程序。

如果是需要调试一个生成core文件并异常终止的程序,可以加上core文件来恢复现场

1
gdb <program> <core dump file>

如果需要调试一个正在运行的程序,可以加上进程ID(一个整数),gdb将会自动以attach模式进行调试

1
gdb <program> <PID>

命令行参数

在程序启动之前可以查看和设置传入main函数的命令行参数args

  • show args:查看当前采用的main函数命令行参数
  • set args:设置main函数命令行参数

例如

1
2
(gdb) set args 10 20
(gdb) show args

执行控制

关于程序执行的调控命令:

  • start:启动程序,在main函数入口处暂停
  • run:运行(直到遇到断点或错误)
  • continue:(假设在断点处暂停)继续执行(直到遇到断点或错误)
  • next:单步逐行执行,如果有函数调用,不会进入调用函数体)
  • step:单步向下执行,如果有函数调用,则会进入调用函数体
  • finish:向上执行,直到跳出函数体

断点(break)

设置断点的命令有很多,可以基于行号,函数名,与当前位置偏移等

1
2
3
4
5
6
7
8
9
10
11
12
13
(gdb) break <line>
(gdb) break 10
// 在第10行设置断点

(gdb) break <function_name>
(gdb) break hello
// 在函数hello入口处设置断点

(gdb) break <file_name>:<line>
// 在指定源文件的指定行设置断点

(gdb) break <function_name>:<line>
// 在指定函数体的指定行设置断点

注意,如果函数体具有重载,则有多种选择,此时可以加上函数参数类型来指定具体的函数,否则gdb会提供候选函数的列表,需要输入编号来确认。

设置临时断点(断点在触发一次后自动失效)

1
(gdb) tbreak ...

设置条件断点(只有满足一定条件才会触发)

1
2
3
(gdb) break ... if <condition>
(gdb) break file_name.cpp:15 if my_variable == 10
// 在指定函数体的指定行设置条件断点,触发要求为my_variable==10

禁用断点(需要首先打印并找到断点的编号)

1
(gdb) disable <breakpoint_id>

启用断点(在禁用之后可以让断点重新生效)

1
(gdb) enable <breakpoint_id>

删除断点(需要首先打印并找到断点的编号)

1
(gdb) delete <breakpoint_id>

查看变量(print)

print可以查看指定表达式的值

1
(gdb) print <expr>

例如

1
2
3
4
5
6
7
8
9
(gdb) print my_variable

(gdb) print var1 var2 var3

(gdb) print my_array[5]

(gdb) print my_struct.member

(gdb) print &my_variable

监视点(display)

监视点可以做到在每次程序暂停时,自动打印指定变量的值,可以避免重复调用print查看变量

1
(gdb) display <expr>

取消对指定变量的监视

1
(gdb) undisplay <expr>

取消所有的监视点

1
(gdb) undisplay

观察点(watchpoints)

除了断点可以用来暂停程序,我们还可以设置观察点来观察一个表达式(变量也是一种表达式)的值是否有了变化,如果发生变化则立刻暂停

设置普通观察点,当表达式的值发生变化时立刻暂停

1
(gdb) watch <expr>

设置读取观察点,当表达式被读时立刻暂停

1
(gdb) rwatch <expr>

设置读取观察点,当表达式被读或者被写时立刻暂停

1
(gdb) awatch <expr>

查看源码(list)

list可以查看当前位置附加的源代码,也可以加上行号或函数名等来显示指定位置的代码

1
2
3
(gdb) list
(gdb) list <line>
(gdb) list <function_name>

指定的行数或者函数入口会显示在正中,默认会显示当前位置前后各5行的源代码,也可以查看和修改显示的总行数

1
2
3
4
5
(gdb) set listsize <num>
// 设置查看的总行数

(gdb) show listsize
// 查看总行数

查看信息(info)

info命令可以查看很多信息,包括断点/变量/监视点/观察点等。

查看所有断点或指定编号的断点

1
2
3
4
(gdb) info break
(gdb) info breakpoints

(gdb) info break <breakpoint_id>

列出当前的所有局部变量

1
(gdb) info locals

列出当前的所有变量

1
(gdb) info variables

查看所有的监视点

1
(gdb) info display

列出当前所有的观察点

1
(gdb) info watchpoints

修改变量

在程序暂停时,可以修改指定变量的值

1
(gdb) set var <my_variable> = <new_value>

例如

1
(gdb) set var x=4

GUI界面

gdb的tui相关命令提供了基于命令行的简单GUI支持,可以在上半屏幕显示源码/汇编等,在下半屏幕使用gdb命令行操作。在GUI模式下,上下键操作会移动GUI上显示的行位置。

可以使用快捷键ctrl+x+a开启和关闭GUI界面。

命令缩写

gdb的很多常见命令都可以等价替换为单个字母的缩写:

  • b=break(断点)
  • c=continue(继续执行)
  • s=step(单步执行,逐语句)
  • n=next(逐过程,跳过函数调用)
  • r=run(运行程序)
  • p=print(打印变量值)
  • d=delete(删除断点)
  • l=list(显示源代码)
  • i=info(显示信息)
  • q=quit(退出 GDB)

退出

quitexit可以退出gdb,如果有正在调试的程序,则会终止程序(需要确认终止)

1
2
(gdb) quit
(gdb) exit