Linux基础(10):linux中GDB调试
GDB下的运行程序,设置断点,打印信息,单步调试等重要知识点
调试准备
想要调试,就必须在编译的时候就在shell命令中加入参数-g,这样才能生成gdb调试信息。同时可额外添加参数-Wall来显示所有的警报, -o0(数字零+字母o)参数来在不影响程序的情况下关掉编译器的优化选项。
1 | #生成不带调试信息的hello可执行文件 |
启动和退出gdb
启动gdb
1 | gdb 可执行程序 |
退出gdb
1 | #在(gdb)状态下输入quit指令或者q,退出gdb调试状态 |
命令行传参
- 一般情境下的命令行传参
1 | gao@gao-VirtualBox:~/桌面/test$ cat hello.c |
- GDB调试下的命令行传参
1 | #第一步gdb先进入可执行程序 |
GDB下运行程序
1 | #两种方式run、start |
1 | #r == run,方法示例 |
1 | #start方法示例 |
1 | #c == continue 继续执行 |
查看代码
- 当前文件
1 | #默认显示main()函数,显示十行代码 |
操作示例
1 | #直接list |
- 切换文件
1 | # 切换到指定的文件,并列出这行号对应的上下文代码, 默认情况下只显示10行内容 |
- 设置显示行数
1 | # 以下两个命令中的 listsize 都可以写成 list |
断点操作
- 设置断点
基础语法
两种断点:普通断点,条件断点
两种文件大类:当前文件,指定文件
- 普通断点的设置
1 | # 在当前文件的某一行上设置断点 |
- 条件断点的设置
1 | # 必须要满足某个条件, 程序才会停在这个断点的位置上 |
操作示例
1 | #设置普通断点 |
- 查看断点
基础语法
1 | #全拼 |
操作示例
1 | (gdb) i b |
1 | Num: 断点的编号,删除断点或者设置断点状态的时候都需要使用 |
- 删除断点
基础语法
1 | # delete == del == d |
操作示例
1 | (gdb) i b |
- 设置断点状态
基础语法
1 | #设置无效 |
操作示例
1 | (gdb) i b |
调试命令
- 继续运行gdb
1 | #当采用start命令阻塞在main(),或者设置断点后,阻塞在断点 |
手动打印信息
基础语法
1
2
3# p == print 直接打印变量
# ptype 打印变量类型
# 打印变量时,整数类型直接默认10进制打印,一些额外设定见下表操作示例
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22(gdb) r
Starting program: /home/gao/桌面/test/hello_gdb 1 2 3 4 5
Breakpoint 1, main (argc=6, argv=0x7fffffffe068) at hello.c:6
6 printf("gcc程序:hello!\n");
(gdb) c
Continuing.
gcc程序:hello!
参数个数: 6
参数 0: /home/gao/桌面/test/hello_gdb
参数 1: 1
参数 2: 2
Breakpoint 2, main (argc=6, argv=0x7fffffffe068) at hello.c:10
10 printf("参数 %d: %s\n", i, argv[i]);
(gdb) p i
$1 = 3
(gdb) p/c i
$2 = 3 '\003'
(gdb) ptype i
type = int
(gdb)自动打印信息
基础语法
- 设置display
1
2
3
4
5
6#进入断点后,设置dislay
# 在变量的有效取值范围内, 自动打印变量的值(设置一次, 以后就会自动显示)
(gdb) display 变量名
# 以指定的整形格式打印变量的值, 关于 fmt 的取值, 请参考 print 命令
(gdb) display/fmt 变量名- 查看显示列表
1
2
3
4
5
6
7
8#显示自动显示列表的命令
# info == i
(gdb) info display
#需要注意的参数
Num : 变量或表达式的编号,GDB 调试器为每个变量或表达式都分配有唯一的编号
Enb : 表示当前变量(表达式)是处于激活状态还是禁用状态,如果处于激活状态(用 y 表示),则每次程序停止执行,该变量的值都会被打印出来;反之,如果处于禁用状态(用 n 表示),则该变量(表达式)的值不会被打印。
Expression :被自动打印值的变量或表达式的名字- 取消&恢复自动显示
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20#删除某个自动显示变量
# 命令中的 num 是通过 info display 得到的编号, 编号可以是一个或者多个
(gdb) undisplay num [num1 ...]
# num1 - numN 表示一个范围
(gdb) undisplay num1-numN
#也可以使用delete命令删除
(gdb) delete display num [num1 ...]
(gdb) delete display num1-numN
#禁用某个自动显示变量
# 命令中的 num 是通过 info display 得到的编号, 编号可以是一个或者多个
(gdb) disable display num [num1 ...]
# num1 - numN 表示一个范围
(gdb) disable display num1-numN
#恢复某个自动显示变量
# 命令中的 num 是通过 info display 得到的编号, 编号可以是一个或者多个
(gdb) enable display num [num1 ...]
# num1 - numN 表示一个范围
(gdb) disable display num1-numN操作示例
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32Breakpoint 3, main (argc=6, argv=0x7fffffffe068) at hello.c:8
8 for(int i= 0; i< argc; ++i)
(gdb) display i
1: i = 0
(gdb) display argv[i]
2: argv[i] = 0x7fffffffe3ac "/home/gao/桌面/test/hello_gdb"
(gdb) i display
Auto-display expressions now in effect:
Num Enb Expression
1: y i
2: y argv[i]
#删除一个display
(gdb) delete display 1
(gdb) i display
Auto-display expressions now in effect:
Num Enb Expression
2: y argv[i]
#禁用一个display
(gdb) disable display 2
(gdb) i display
Auto-display expressions now in effect:
Num Enb Expression
2: n argv[i]
#恢复一个display
(gdb) ena display 2
(gdb) i display
Auto-display expressions now in effect:
Num Enb Expression
2: y argv[i]单步调试
基本语法
step命令
基本语法:进入断点后,单步调试, 当遇到函数体时,会进入到函数体内部。
1
2
3
4# 从当前代码行位置, 一次调试当前行下的每一行代码
# step == s
# 如果这一行是函数调用, 执行这个命令, 就可以进入到函数体的内部
(gdb) stepfinish命令
基本语法:当step进入到函数体内部后,想要跳出函数体,直接使用finsh命令跳出函数体,若函数体内部有断点,需要先将断点删除或者设置为无效,之后再使用finish命令才能正确跳出函数体
1
2# 如果通过 s 单步调试进入到函数内部, 想要跳出这个函数体
(gdb) finishnext命令
基本语法:进入断点后,单步调试, 当遇到函数体时,直接跳过函数体,不进入内部。
1
2
3# next == n
# 如果这一行是函数调用, 执行这个命令, 不会进入到函数体的内部
(gdb) nextuntil命令
基本语法:在单步调试时,通过until命令可以跳出某个循环体,节省调试时间,但是需要满足以下的条件
- 要跳出的循环体内部不能有有效的断点
- 必须要在循环体开始/结束行执行该命令
1
(gdb) until
操作示例
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24(gdb) r
Starting program: /home/gao/桌面/test/hello_gdb 1 2 3 4 5
Breakpoint 1, main (argc=6, argv=0x7fffffffe068) at hello.c:6
6 printf("gcc程序:hello!\n");
(gdb) c
Continuing.
gcc程序:hello!
参数个数: 6
Breakpoint 2, main (argc=6, argv=0x7fffffffe068) at hello.c:8
(gdb) n
参数 0: /home/gao/桌面/test/hello_gdb
8 for(int i= 0; i< argc; ++i)
1: i = 0
##################################调用until命令直接跳过循环体
(gdb) until
参数 1: 1
参数 2: 2
参数 3: 3
参数 4: 4
参数 5: 5
12 return 0;设置变量值
基本语法:在调试中,需要观察某个变量在特殊值下的情况,但是达到特殊值条件比较慢或者是,特殊值很难得到,可以采用设置变量值的方式,人为设计达到特殊值
1
2
3# 可以在循环中使用, 直接设置循环因子的值
# 假设某个变量的值在程序中==90的概率是5%, 这时候可以直接通过命令将这个变量值设置为90
(gdb) set var 变量名=值操作示例
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41(gdb) r
Starting program: /home/gao/桌面/test/hello_gdb 1 2 3 4 5
Breakpoint 1, main (argc=6, argv=0x7fffffffe068) at hello.c:6
6 printf("gcc程序:hello!\n");
################################# next命令,单步调试
(gdb) n
gcc程序:hello!
7 printf("参数个数: %d\n",argc);
(gdb) n
参数个数: 6
Breakpoint 2, main (argc=6, argv=0x7fffffffe068) at hello.c:8
8 for(int i= 0; i< argc; ++i)
1: i = 0
(gdb) n
10 printf("参数 %d: %s\n", i, argv[i]);
1: i = 0
(gdb) p i
$3 = 0
################################ 设置变量值
(gdb) set var i=4
(gdb) n
参数 4: 4
8 for(int i= 0; i< argc; ++i)
1: i = 4
(gdb) n
10 printf("参数 %d: %s\n", i, argv[i]);
1: i = 5
(gdb) n
参数 5: 5
8 for(int i= 0; i< argc; ++i)
1: i = 5
(gdb) n
12 return 0;
(gdb) c
Continuing.
[Inferior 1 (process 13098) exited normally]