LV02-04-GDB-调试工具
本文主要是GDB——调试工具的相关笔记,若笔记中有错误或者不合适的地方,欢迎批评指正😃。
点击查看使用工具及版本
Windows | windows11 |
Ubuntu | Ubuntu16.04的64位版本 |
VMware® Workstation 16 Pro | 16.2.3 build-19376536 |
SecureCRT | Version 8.7.2 (x64 build 2214) - 正式版-2020年5月14日 |
开发板 | 正点原子 i.MX6ULL Linux阿尔法开发板 |
uboot | NXP官方提供的uboot,NXP提供的版本为uboot-imx-rel_imx_4.1.15_2.1.0_ga(使用的uboot版本为U-Boot 2016.03) |
linux内核 | linux-4.15(NXP官方提供) |
STM32开发板 | 正点原子战舰V3(STM32F103ZET6) |
点击查看本文参考资料
参考方向 | 参考原文 |
--- | --- |
一、调试器
程序中的错误主要分为两类,分别为语法错误和逻辑错误:程序中的语法错误几乎都可以由编译器诊断出来,很容易就能发现并解决;逻辑错误指的是代码思路或者设计上的缺陷,程序出现逻辑错误的症状是:代码能够编译通过,没有语法错误,但是运行结果不对。对于这类错误,只能靠我们自己去发现和纠正。
程序中出现的语法错误可以借助编译器解决;但逻辑错误则只能靠自己解决,而解决逻辑错误最高效的方法,就是借助调试工具对程序进行调试。
比如,让代码一行一行运行,而不是一次性全部运行,让程序停在某个地方,然后查看当前变量的值,或者内存中的数据,这样我们不就可以监控程序运行的每个细节了吗,哪里出了问题,也会很快找到。
编译程序需要借助专业的编译器,调试程序也需要借助专业的辅助工具,即调试器。
点击查看常见调试器
Remote Debugger | Remote Debugger 是 VC/VS 自带的调试器,与整个IDE无缝衔接,使用非常方便。 |
WinDbg | 大名鼎鼎的 Windows 下的调试器,它的功能甚至超越了 Remote Debugger,它还有一个命令行版本(cdb.exe),但是这个命令行版本的调试器指令比较复杂。 |
LLDB | XCode 自带的调试器,Mac OS X 下开发必备调试器。 |
GDB | Linux下使用最多的一款调试器,也有 Windows 的移植版。 |
二、GDB 是什么?
GDB 全称 GNU symbolic debugger ,诞生于 GNU 计划(同时诞生的还有 GCC 、 Emacs 等),是 Linux 下常用的程序调试器。当下的 GDB 支持调试多种编程语言编写的程序,包括 C 、 C++ 、 Go 、 Objective-C 、 OpenCL 、 Ada 等。我们可以通过 GDB 调试器实现以下几个功能:
- 程序启动时,可以按照我们自定义的要求运行程序,例如设置参数和环境变量;
- 可使被调试程序在指定代码处暂停运行,并查看当前程序的运行状态(例如当前变量的值,函数的执行结果等),即支持断点调试;
- 程序执行过程中,可以改变某个变量的值,还可以改变代码的执行顺序,从而尝试修改程序中出现的逻辑错误。
GDB官网 | https://sourceware.org/gdb |
三、GDB的使用
【说明】后边输入的命令中,带有 (gdb) 的表示在gdb调试器中输入的命令。
1.基本使用
1.1文件处理
在使用 GDB 之前,需要对程序编译进行处理,需要加上 -g 参数才能生成满足 GDB 要求的可执行文件。例如
1 | gcc -g test.c -o test |
这样生成的可执行文件才可以正常使用 GDB 进行调试。
【注意】
(1)只有在代码处于运行或暂停状态时才能查看变量值。
(2)设置断点后程序在指定行之前停止 。
1.2启动 GDB 调试器
当生成满足要求的可执行程序后,可以如下命令启动调试器:
1 | gdb <filename> # 这是在shell中执行的命令 |
- filename :生成的可执行程序名称。
【注意】 gdb 在执行完一个命令后不输入任何命令直接回车, gdb 会默认执行上一个命令。
输入下边命令后会发现终端输出以下信息:
1 | GNU gdb (Ubuntu 8.1.1-0ubuntu1) 8.1.1 |
这是是一些提示信息,我们按下 Enter 就可以继续进入调试器,而按下 q 按键就可以退出调试器。进入调试器后,命令格式如下:
1 | (gdb) <command> |
(gdb) 是提示符,就像 Shell 中的 # 和 $ 一样,后边跟上命令即可。
1.3关闭调试器
我们调试结束了,需要退出调试器的时候我们可以使用如下命令退出:
1 | (gdb) q |
然后按下 Enter 按键就可以退出 gdb 调试了。
2.基本调试命令
2.1 list 命令
在调试器中可缩写为 l ,可以列出所调试程序的代码。使用格式如下:
1 | (gdb) l [option] |
【参数说明】
option | 说明 |
省略 | gbd会将gdb当前所处的行以及后面的代码打印在屏幕上。 |
lineNumber | 打印指定行附近的代码。如list 8,gdb会将8行前后的代码打印在屏幕上。 |
- | gbd会将gdb当前所处的行前面的代码打印在屏幕上。 |
functionName | 打印名称为functionName的函数的上下文的代码。 |
2.2 run 命令
在调试器中可缩写为 r ,可以开始运行程序,若无断点,则会全部执行一遍,遇到第一个断点会停下。使用格式如下:
1 | (gdb) r |
2.3 break 命令
在调试器中可缩写为 b ,用于设置断点。使用格式如下:
1 | (gdb) b [option] |
【参数说明】
option | 说明 |
lineNumber | 在指定的代码行打断点 |
filename:lineNumber | gbd会在名称为filename的文件中的第lineNumber行打断点。 |
filename:function | gdb在名称为filename的文件中的function函数入口处打断点。 |
*address | gdb在在程序运行的内存地址处打断点。 |
2.4 info 命令
用于查看断点信息,使用格式如下:
1 | (gdb) info b # 查看所有断点信息 |
断点信息显示格式如下:
1 | Num Type Disp Enb Address What |
2.5 delete 命令
用于删除断点。使用格式如下:
1 | (gdb) delete b # 删除所有断点信息 |
2.6 next 命令
在调试器中可缩写为 n ,用于单步运行程序,但是如果有函数调用的话,它不会进入函数体。使用格式如下:
1 | (gdb) n [option] |
【参数说明】
option | 说明 |
省略 | 一条一条单步执行。 |
count | 执行后边的count条指令。 |
2.7 step 命令
在调试器中可缩写为 s ,用于单步运行程序,如果有函数调用的话,它会进入函数体。使用格式如下:
1 | (gdb) s [option] |
【参数说明】
option | 说明 |
省略 | 一条一条单步执行。 |
n | 执行后边的 n 条指令。 |
2.8 until 命令
在调试器中可缩写为 u ,用于运行到某一行程序或者直接运行完循环,如果有函数调用的话,它会进入函数体。使用格式如下:
1 | (gdb) u [option] |
【参数说明】
option | 说明 |
省略 | 可以使 GDB调试器快速运行完当前的循环体,并运行至循环体外停止。 |
n | 某行代码的行号,以指示 GDB 调试器直接执行至指定位置后停止。 |
【注意】until 命令并非任何情况下都会发挥这个作用,只有当执行至循环体尾部(最后一行代码)时,until 命令才会发生此作用;反之,until 命令和 next 命令的功能一样,只是单步执行程序。
2.9 continue 命令
在调试器中可缩写为 c ,用于继续运行程序。当程序在某一断点处停止后,用该指令可以继续执行,直至遇到断点或者程序结束。使用格式如下:
1 | (gdb) c |
2.10 print
2.10.1查看变量
在调试器中可缩写为 p ,用于继打印指定变量的值。使用格式如下:
1 | (gdb) p [option --][/fmt] expr |
【参数说明】
- option :表示该命令所支持的选项,这些选项可以控制 print 命令输出指定内容的变量或者表达式的值,是可选的;
点击查看 option
option | 说明 |
-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 :指定输出变量或表达式值时所采用的格式,是可选的;
点击查看 fmt
/fmt | 功 能 |
/x | 以十六进制的形式打印出整数。 |
/d | 以有符号、十进制的形式打印出整数。 |
/u | 以无符号、十进制的形式打印出整数。 |
/o | 以八进制的形式打印出整数。 |
/t | 以二进制的形式打印出整数。 |
/f | 以浮点数的形式打印变量或表达式的值。 |
/c | 以字符形式打印变量或表达式的值。 |
例如,
1 | (gdb) p /x a # 以十六进制显示变量a的值 |
- expr :指定要查看的变量或表达式。
【注意】
(1)options 参数和 /fmt 或者 expr 之间,必须用 -- ( 2 个 - 字符)分隔。
(2)[ ] 里边的内容是可选的,可以没有。
2.10.2修改变量值
print 命令还支持修改变量值,一般格式如下:
1 | (gdb) print variable=value |
- variable :表示要修改的变量名。
- value :表示修改后的值。
2.10.3查看数组
print 命令还支持 @ 运算符,多用于查看数组,一般格式如下:
1 | (gdb) print first@len |
- first :用于指定数组查看区域内的首个元素的值。
- len :用于指定自 first 元素开始查看的元素个数。
2.10.4查看不同文件同名变量
当程序中包含多个作用域不同但名称相同的变量或表达式时,可以借助 :: 运算符明确指定要查看的目标变量或表达式。:: 运算符的语法格式如下:
1 | (gdb) print file::variable |
file :用于指定具体的文件名。
funciton :用于指定具体所在函数的函数名。
variable :表示要查看的目标变量或表达式。
3.多进程调试
此部分为后边学习多进程编程后补充而来。注意这里的命令我前边都加了 $ ,表示在终端中的标识,下边的 (gdb) 表示在调试器中的命令前缀标识。
- (1)文件编译处理
1 | gcc -g test.c -o test |
- (2)启动 GDB
1 | gdb test |
- (3)开始调试
1 | (gdb) start # 单步运行程序,程序会在第一条功能语句停下来。 |
- (4)设置调试子进程或者父进程
1 | (gdb) set follow-fork-mode child # 设置GDB调试子进程 |
- (5)设置调试多个进程
1 | (gdb) set detach-on-fork on/off # 设置GDB跟踪调试单个进程或多个 |
on 表示只调试父进程或子进程的其中一个,(根据 follow-fork-mode 来决定),这是默认的模式。off 表示父子进程都在 gdb 的控制之下,其中一个进程正常调试(根据 follow-fork-mode 来决定),另一个进程会被设置为暂停状态。然后我们可以通过下边的命令显示可以调试的所有进程:
1 | (gdb) info inferiors # 显示GDB调试的进程 |
如下图所示:
随后我们就可以从上边显示的信息中心选择自己需要调试的进程号,进行指定进程的调试:
1 | (gdb) inferiors <进程序号1,2,3...> # 切换GDB调试的进程 |
4.多线程调试
此部分为后边学习多线程编程后补充而来。注意这里的命令我前边都加了 $ ,表示在终端中的标识,下边的 (gdb) 表示在调试器中的命令前缀标识。。
- (1)文件编译处理
1 | gcc -g test.c -o test |
- (2)启动 GDB
1 | gdb test |
- (3)开始调试
1 | (gdb) start # 单步运行程序,程序会在第一条功能语句停下来。 |
- (4)显示所有线程
1 | (gdb) info thread |
- (5)切换线程
1 | (gdb) thread <id> |
- (6)为指定线程添加断点
1 | (gdb) break location thread <id> |
- (7)设置线程锁
1 | (gdb) set scheduler-locking on/off # on 表示其他线程会暂停,可以单独调试一个线程 |
四、TUI 模式
先上一张图吧,简单来说就是可以对照源码进行调试,这就是 GDB 自带的 GDB TUI ,它看起来比直接使用 list 要更美观一点:
1.启动方式
- 直接使用命令启动
1 | gdbtui -q 需要调试的程序名 |
- 快捷键
1 | Ctrl + x + a |
正常开启 GDB 调试,然后按快捷键就可以启动啦。
2.相关命令
- (1) layout 命令
1 | 命令 |
- (2) focus 命令
在默认设置下,方向键和 PageUp/PageDown 都是用来控制 GDB TUI 的 src 窗口的,所以我们在终端中常用上下键显示前一条命令和后一条命令的功能就没有了。但是我们可以通过命令将窗口焦点移动到命令窗口,这样就可以啦。我们可以在 GDB 调试中查看 focus 命令的帮助如下:
1 | (gdb) help focus |
其中的 WINDOW-NAME 可选的有如下几种:
1 | src : the source window |
当我们执行:
1 | (gdb) help cmd |
这个时候,窗口焦点就在命令行窗口啦,我们就可以正常使用上下键来选择历史命令啦