9.GDB调试
gdb 是用来调试解决bug的工具 ,
GDB是一套字符界面的程序集,可以使用命令 gdb 加载要调试的程序
同gcc配套组成一套完整的开发环境,可移植性好。gdb/gcc 是Linux和类Unix系统的标准开发环境。
9.1 调试前准备
如果项目程序为了调试而编译,必须打开调试选项(-g)
不影响程序的情况下 关闭编译器优化选择(-O0)
打开所有warning (可以避免一些bug) (-Wall)
gcc -g hello.c -o Hello
gcc hello.c -o Hello2
9.2 启动 推出 传参 gdb
9.2.1 启动gdb
gdb是一个程序用于调式的进程,需要先打开!gdb进程启动之后 ,需要被调试的程序并没有执行,打开终端,切换到被调试程序路径,执行如下命令
$ gdb 可执行程序名字
$ gdb Hello
(gdb)
9.2.2 命令行传参
部分程序在启动时需要传入命令行参数 ,在调试这类程序时必须在 程序启动前 通过调试程序的gdb进程传递进去(set args .....) , 执行以下命令
举例需要传参函数
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#define NUM 10
int main(int argc, char* argv[])
{
printf("参数个数: %d\n", argc);
for(int i=0; i<argc; ++i)
{
printf("%d\n", NUM);
printf("参数 %d: %s\n", i, argv[i]);
}
return 0;
}
$ gcc -g args.c -o app
$ gdb app
(gdb)
$ (gdb) set args 参数1 参数2 ...........
(gdb) show args
操作实例
liu@liu-Ubuntu:~/StutyLinux/GDB$ gcc -g args.c -o app
liu@liu-Ubuntu:~/StutyLinux/GDB$ gdb app
GNU gdb (Ubuntu 8.1.1-0ubuntu1) 8.1.1
Copyright (C) 2018 Free Software Foundation, Inc.
For help, type "help".
Type "apropos word" to search for commands related to "word"...
Reading symbols from app...done.
(gdb) set args 1 abc
(gdb) show args
Argument list to give program being debugged when it is started is "1 abc".
9.2.3 gdb 中启动 、退出
在gdb中启动需要调试的应用程序有两种方式 :run 命令 、 start 命令
在整个gdb调试过程中 ,启动命令只能使用一次
run : 缩写 r ,如果程序中设置断点,会停在第一个断点处,如没有断点,程序执行完start: 启动程序 , 阻塞在 main 函数第一行 ,等待输入的后续其他gdb命令continue: 缩写 c ,在使用start命令后,或在断点处继续运行 , 可以使用 continue 命令quit: 缩写 q , 退出gdb调试,终止gdb线程
liu@liu-Ubuntu:~/StutyLinux/GDB$ gdb app
GNU gdb (Ubuntu 8.1.1-0ubuntu1) 8.1.1
Copyright (C) 2018 Free Software Foundation, Inc.
Reading symbols from app...done.
(gdb) set args 1 abc
(gdb) show args
Argument list to give program being debugged when it is started is "1 abc".
(gdb) start
Temporary breakpoint 1 at 0x659: file args.c, line 13.
Starting program: /home/liu/StutyLinux/GDB/app 1 abc
Temporary breakpoint 1, main (argc=3, argv=0x7fffffffdf38) at args.c:13
13 printf("参数个数: %d\n", argc);
(gdb) c
Continuing.
参数个数: 3
10
参数 0: /home/liu/StutyLinux/GDB/app
10
参数 1: 1
10
参数 2: abc
(gdb)q
9.3 查看代码
gdb调试没有IDE完善的可视化窗口*(IDE牛逼,将对应可执行文件和源码对应可视化)* , 默认情况下通过 list ,查看位于入口函数 main 对应的文件代码信息 , 如果不切换main函数 所在的文件就是当前文件,反之切换文件就对于当前文件夹
9.3.1 当前文件夹
项目中有很多文件夹, 默认情况下,gdb 通过 list 查看的是位于入口函数 main 对于的文件
(gdb) list
(gdb) list 行号
(gdb) list 函数名
通过 list 去查看文件代码,默认只显示 10 行,如果还想继续查看后边的内容,可以继续执行 list 命令,也可以直接回车(再次执行上一次执行的那个 gdb 命令)。
9.3.2 切换文件
查看文件内容的时候,需要频繁切换文件,在 list 命令后指定需要查看的文件名 ,在完成此操作后,这个文件会变成当前文件
(gdb) list 文件名 :行号
(gdb) list 文件名 :函数名
9.3.2 修改默认查看行数
默认查看行数 10 行,如果想继续查看后面内容 ,回车键(再次执行上次操作)
当然也可以修改默认一次显示行数 ,set listsize 命令 可以缩写为 set list ,
查看当前 一次显示行数 show listsize 命令 ,可以缩写 show list
(gdb) show list
(gdb) set list 行数
9.4 断点操作
在程序调式中,调试某一行或得到某一个变量的运行状态下的实际值 ,需要在这一行设置断点,程序运行到此行会阻塞,在通过gdb 的调试命令获取信息。
设置断点的命令 break ,或缩写 b
9.4.1 设置断点
断点可以设置在某一行 或 某个函数上 ,断点设置有两种方式 :常规断点 、 条件断点
常规断点: 程序运行到这个位置就会被阻塞条件断点: 只有满足指定的条件才会在此断点阻塞
设置普通断点
(gdb) b 行号1 行号2 / 行数1 - 行数2
(gdb) b 函数名
(gdb) b 文件名 :行号
(gdb) b 文件名 :函数名
设置条件断点
(gdb) b 行数 if 变量名==某个值
9.4.2 查看断点
断点设置完成之后,可通过 info break 命令 查看设置的断点信息 ,可以缩写为 i b
(gdb) i b
(gdb) i b
Num Type Disp Enb Address What
1 breakpoint keep y 0x0000000000400cb5 in main() at test.cpp:12
2 breakpoint keep y 0x0000000000400cbd in main() at test.cpp:13
3 breakpoint keep y 0x0000000000400cec in main() at test.cpp:18
4 breakpoint keep y 0x00000000004009a5 in insertionSort(int*, int)
at insert.cpp:8
5 breakpoint keep y 0x0000000000400cdd in main() at test.cpp:16
6 breakpoint keep y 0x00000000004009e5 in insertionSort(int*, int)
at insert.cpp:16
断点信息中重点属性
Num: 断点的编号,删除断点或者设置断点状态的时候都需要使用(重要)Enb: 当前断点的状态,y 表示断点可用,n 表示断点不可用What: 描述断点被设置在了哪个文件的哪一行或者哪个函数上
9.4.3 删除断点
如果不需要某一个断点,可以通过 delete 断点编号 ,其中 delete 可以简写为 del / d
删除方式两种: 删除(一个或多个)指定断点 、 删除一个连续的断点区间
(gdb) d 断点编号1 断点编号2 断点编号3 .。。。。。
(gdb) d num1 - num5
9.4.4 设置断点状态
相比删除断点,设置断点状态更加灵活 , 在不需要断点时可将其状态设置为 不可用状态,设置命令为disable 断点编号 ; 在需要的时候在将其设置为可用状态,设置命令为 enable 断点编号
设置断点无效
(gdb) disable 断点编号1 断点编号2 ......
(gdb) dis num1 - numN
无效断点生效
(gdb) i b
(gdb) enable 断点编号1 断点编号2 ......
(gdb) ena num1 - numN
9.5 调试命令
当调试的程序在断点阻塞但是又希望程序继续执行时, 可以使用 continue 命令 , 缩写 c ,程序会继续执行,知道遇到下一个有效断点
(gdb) continue
9.5.1 手动打印变量信息
程序阻塞后,可以通过 print 命令 ,缩写 p 打印变量的名字或变量类型 ,并且跟踪打印某变量的值
打印变量值
在gdb调试的时候如需要打印变量的值 ,如打印的变量是整数 还可以指定输出的整数格式;
格式化输出的整数对应的字符表如下:
格式化字符 | 说明 |
---|
/x | 以十六进制的形式打印出整数。 | /d | 以有符号、十进制的形式打印出整数 | /u | 以无符号、十进制的形式打印出整数。 | /o | 以八进制的形式打印出整数。 | /t | 以二进制的形式打印出整数。 | /f | 以浮点数的形式打印变量或表达式的值。 | /c | 以字符形式打印变量或表达式的值。 |
(gdb) p 变量名
(gdb) p/t 变量名
举例
(gdb) p i
$5 = 3
(gdb) p/x i
$6 = 0x3
(gdb) p/o i
$7 = 03
打印变量类型
如果调试过程中需要获得某变量名的类型 , 可以通过 ptype 命令
(gdb) ptype 变量名
(gdb) ptype i
type = int
(gdb) ptype array[i]
type = int
(gdb) ptype array
type = int [12]
9.5.2 自动打印信息
display 命令 用于调试时查看变量或表达式的值 , 和print的区别是每当程序暂停执行(例如单步执行)时,GDB 调试器都会自动帮我们打印出来,而 print 命令则不会。因此,当我们想频繁查看某个变量或表达式的值从而观察它的变化情况时,使用 display 命令可以一劳永逸。display 命令没有缩写形式,常用的语法格式如下 2 种:
(gdb) display 变量名
(gdb) display/f 变量名
查看自动显示列表
使用 display 自动显示的变量都会记录在一张列表中 , 通过 info diaplay 命令 查看该列表信息
(gdb) info display
Auto-display expressions now in effect:
Num Enb Expression
1: y i
2: y array[i]
3: y /x array[i]
其中参数含义:
Num : 变量或表达式的编号,GDB 调试器为每个变量或表达式都分配有唯一的编号Enb : 表示当前变量(表达式)是处于**激活状态还是禁用状态,**如果处于激活状态(用 y 表示),则每次程序停止执行,该变量的值都会被打印出来;反之,如果处于禁用状态(用 n 表示),则该变量(表达式)的值不会被打印。Expression : 被自动打印值的变量或表达式的名字。
取消自动显示
当不在需要打印变量/表达式的值 , 可以将其删除或禁用
删除自动显示的变量
(gdb) undisplay num [num1 ...]
(gdb) undisplay num1-numN
(gdb) delete display num [num1 ...]
(gdb) delete display num1-numN
禁止自动显示状态
当不想删除自动显示变量,可以通过更灵活的方式改变其状态,达到相同目的
(gdb) disable display num [num1 ...]
(gdb) disable display num1-numN
启动自动显示状态
(gdb) enable display num [num1 ...]
(gdb) disable display num1-numN
9.6 单步调试
当程序阻塞在某个断点上后, 可以通过以下命令单步调试
step 命令
step 命令 可缩写为 s ,该命令被执行一次代码向下执行一行,如果这一行被函数调用,程序进入函数体内部
(gdb) step
next 命令
next 命令 缩写 n ,该命令和 step 命令很相似 ,但是使用next不会进入函数体内部
(gdb) next
finsh 命令
如果通过单步调试进入函数体内部,想要跳出函数体,可以通过 finish 命令 如果想跳出函数体 ,需要保证函数体内不能有有效断点 ,否则无法跳出
(gdb) finish
until 命令
通过until 命令 可以直接跳出某个循环体 ,提高调试效率
使用时需要满足条件:
- 需要跳出的循环体内部不能有有效的断点
- 必须在循环体的开始/结束行执行该命令
(gdb) until
设置变量值调试
在调试程序的时候,我们需要在某个变量等于某个特殊值的时候查看程序的运行状态,但是通过程序运行让变量等于这个值又非常困难,这种情况下就可以在 gdb 中直接对这个变量进行值的设置,或者是在单步调试的时候通过设置循环因子的值直接跳出某个循环,值设置的命令格式为: set var 变量名=值
(gdb) set var 变量名=值
=
如果通过单步调试进入函数体内部,想要跳出函数体,可以通过 finish 命令 如果想跳出函数体 ,需要保证函数体内不能有有效断点 ,否则无法跳出
(gdb) finish
until 命令
通过until 命令 可以直接跳出某个循环体 ,提高调试效率
使用时需要满足条件:
- 需要跳出的循环体内部不能有有效的断点
- 必须在循环体的开始/结束行执行该命令
(gdb) until
设置变量值调试
在调试程序的时候,我们需要在某个变量等于某个特殊值的时候查看程序的运行状态,但是通过程序运行让变量等于这个值又非常困难,这种情况下就可以在 gdb 中直接对这个变量进行值的设置,或者是在单步调试的时候通过设置循环因子的值直接跳出某个循环,值设置的命令格式为: set var 变量名=值
(gdb) set var 变量名=值
|