0%

Linux中GDB调试

Linux基础(10):linux中GDB调试

GDB下的运行程序,设置断点,打印信息,单步调试等重要知识点

调试准备

想要调试,就必须在编译的时候就在shell命令中加入参数-g,这样才能生成gdb调试信息。同时可额外添加参数-Wall来显示所有的警报, -o0(数字零+字母o)参数来在不影响程序的情况下关掉编译器的优化选项。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#生成不带调试信息的hello可执行文件
gao@gao-VirtualBox:~/桌面/test$ gcc hello.c -o hello
gao@gao-VirtualBox:~/桌面/test$ ls
d hello hello.c hello.cpp make_file new_file.txt
gao@gao-VirtualBox:~/桌面/test$ ./hello
gcc程序:hello!

#生成带有调试信息的hello_gdb可执行文件
gao@gao-VirtualBox:~/桌面/test$ gcc hello.c -g -Wall -o0 -o hello_gdb
gao@gao-VirtualBox:~/桌面/test$ ls
d hello hello.c hello.cpp hello_gdb make_file new_file.txt
gao@gao-VirtualBox:~/桌面/test$ ./hello_gdb
gcc程序:hello!

#文件差别
-rwxrwxr-x 1 gao gao 17K 6月 23 16:25 hello #不可用于GDB调试
-rwxrwxr-x 1 gao gao 19K 6月 23 16:26 hello_gdb #可用于GDB调试

#-g参数是必备的

启动和退出gdb

启动gdb

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
gdb 可执行程序
#此时gdb启动,但可执行程序没有启动

具体示例
gao@gao-VirtualBox:~/桌面/test$ gdb hello_gdb
GNU gdb (Ubuntu 9.2-0ubuntu1~20.04) 9.2
Copyright (C) 2020 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.
Type "show copying" and "show warranty" for details.
This GDB was configured as "x86_64-linux-gnu".
Type "show configuration" for configuration details.
For bug reporting instructions, please see:
<http://www.gnu.org/software/gdb/bugs/>.
Find the GDB manual and other documentation resources online at:
<http://www.gnu.org/software/gdb/documentation/>.

For help, type "help".
Type "apropos word" to search for commands related to "word"...
Reading symbols from hello_gdb...
(gdb)
#从这时候开始,进入gdb的程序

退出gdb

1
2
#在(gdb)状态下输入quit指令或者q,退出gdb调试状态
(gdb) quit

命令行传参

  • 一般情境下的命令行传参
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
gao@gao-VirtualBox:~/桌面/test$ cat hello.c
#include <stdio.h>
//using namespace std

int main(int argc, char* argv[])
{
printf("gcc程序:hello!\n");
printf("参数个数: %d\n",argc);
for(int i= 0; i< argc; ++i)
{
printf("参数 %d: %s\n", i, argv[i]);
}
return 0;
}

gao@gao-VirtualBox:~/桌面/test$ gcc -g -o0 -Wall hello.c -o hello_gdb

#常规运行下,命令行传参
gao@gao-VirtualBox:~/桌面/test$ ./hello_gdb 1 2 3 4 5
gcc程序:hello!
参数个数: 6
参数 0: ./hello_gdb
参数 1: 1
参数 2: 2
参数 3: 3
参数 4: 4
参数 5: 5

  • GDB调试下的命令行传参
1
2
3
4
5
6
7
8
9
10
#第一步gdb先进入可执行程序
gao@gao-VirtualBox:~/桌面/test$ gdb hello_gdb

#(gdb)下set args 参数1 参数2 ...
(gdb) set args 1 2 3 4 5

#可以通过show args命令显示已经输入的参数
(gdb) show args
Argument list to give program being debugged when it is started is "1 2 3 4 5".
(gdb)

GDB下运行程序

1
2
3
4
5
#两种方式run、start
#run直接运行到断点位置停止
#start运行到main函数第一行就停止

#阻塞之后,用continue指令继续执行
1
2
3
4
5
6
7
8
9
10
11
#r == run,方法示例
(gdb) r
Starting program: /home/gao/桌面/test/hello_gdb 1 2 3 4 5
gcc程序:hello!
参数个数: 6
参数 0: /home/gao/桌面/test/hello_gdb
参数 1: 1
参数 2: 2
参数 3: 3
参数 4: 4
参数 5: 5
1
2
3
4
5
6
7
#start方法示例
(gdb) start
Temporary breakpoint 1 at 0x555555555169: file hello.c, line 5.
Starting program: /home/gao/桌面/test/hello_gdb 1 2 3 4 5

Temporary breakpoint 1, main (argc=21845, argv=0x0) at hello.c:5
5 {
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#c == continue  继续执行
(gdb) start
Temporary breakpoint 1 at 0x555555555169: file hello.c, line 5.
Starting program: /home/gao/桌面/test/hello_gdb 1 2 3 4 5

Temporary breakpoint 1, main (argc=21845, argv=0x0) at hello.c:5
5 {
(gdb) c
Continuing.
gcc程序:hello!
参数个数: 6
参数 0: /home/gao/桌面/test/hello_gdb
参数 1: 1
参数 2: 2
参数 3: 3
参数 4: 4
参数 5: 5
[Inferior 1 (process 12650) exited normally]
(gdb)

查看代码

  • 当前文件
1
2
3
4
5
6
7
8
9
10
#默认显示main()函数,显示十行代码
#list == l
(gdb) list
#想要继续显示,直接enter键

#list 行号
这一行为中间,显示十行

#list 函数
显示这个函数内容十行

操作示例

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
#直接list
(gdb) list
1 #include <stdio.h>
2 //using namespace std
3
4 int main(int argc, char* argv[])
5 {
6 printf("gcc程序:hello!\n");
7 printf("参数个数: %d\n",argc);
8 for(int i= 0; i< argc; ++i)
9 {
10 printf("参数 %d: %s\n", i, argv[i]);
(gdb)

#list 行号
(gdb) l 6
1 #include <stdio.h>
2 //using namespace std
3
4 int main(int argc, char* argv[])
5 {
6 printf("gcc程序:hello!\n");
7 printf("参数个数: %d\n",argc);
8 for(int i= 0; i< argc; ++i)
9 {
10 printf("参数 %d: %s\n", i, argv[i]);

#list 函数名
(gdb) list main
1 #include <stdio.h>
2 //using namespace std
3
4 int main(int argc, char* argv[])
5 {
6 printf("gcc程序:hello!\n");
7 printf("参数个数: %d\n",argc);
8 for(int i= 0; i< argc; ++i)
9 {
10 printf("参数 %d: %s\n", i, argv[i]);
  • 切换文件
1
2
3
4
5
# 切换到指定的文件,并列出这行号对应的上下文代码, 默认情况下只显示10行内容
(gdb) l 文件名:行号

# 切换到指定的文件,并显示这个函数的上下文内容, 默认显示10行
(gdb) l 文件名:函数名
  • 设置显示行数
1
2
3
4
5
# 以下两个命令中的 listsize 都可以写成 list
(gdb) set listsize 行数

# 查看当前list一次显示的行数
(gdb) show listsize

断点操作

  • 设置断点

基础语法

两种断点:普通断点,条件断点

两种文件大类:当前文件,指定文件

  • 普通断点的设置
1
2
3
4
5
6
7
8
# 在当前文件的某一行上设置断点
# break == b
(gdb) b 行号
(gdb) b 函数名 # 停止在函数的第一行

# 在非当前文件的某一行上设置断点
(gdb) b 文件名:行号
(gdb) b 文件名:函数名 # 停止在函数的第一行
  • 条件断点的设置
1
2
3
# 必须要满足某个条件, 程序才会停在这个断点的位置上
# 通常情况下, 在循环中条件断点用的比较多
(gdb) b 行数 if 变量名==某个值

操作示例

1
2
3
4
5
6
7
#设置普通断点
(gdb) b 8
Breakpoint 1 at 0x119e: file hello.c, line 8.

#设置条件断点
(gdb) b 10 if i==2
Breakpoint 2 at 0x11a7: file hello.c, line 10.
  • 查看断点

基础语法

1
2
3
4
#全拼
info break
#简写,i == info
i b

操作示例

1
2
3
4
5
(gdb) i b
Num Type Disp Enb Address What
1 breakpoint keep y 0x000000000000119e in main at hello.c:8
2 breakpoint keep y 0x00000000000011a7 in main at hello.c:10
stop only if i==2
1
2
3
Num: 断点的编号,删除断点或者设置断点状态的时候都需要使用
Enb: 当前断点的状态,y 表示断点可用,n 表示断点不可用
What: 描述断点被设置在了哪个文件的哪一行或者哪个函数上
  • 删除断点

基础语法

1
2
3
4
5
6
7
8
9
10
11
# delete == del == d
# 需要 info b 查看断点的信息, 第一列就是编号

# 举例:
(gdb) d 1 # 删除第1个断点
(gdb) d 2 4 6 # 删除第2,4,6个断点

# 删除一个范围, 断点编号 num1 - numN 是一个连续区间
(gdb) d num1-numN
# 举例, 删除第1到第5个断点
(gdb) d 1-5

操作示例

1
2
3
4
5
6
7
8
9
10
11
12
13
(gdb) i b
Num Type Disp Enb Address What
1 breakpoint keep y 0x000055555555519e in main at hello.c:8
breakpoint already hit 1 time
2 breakpoint keep y 0x00005555555551a7 in main at hello.c:10
stop only if i==2

(gdb) d 1 #删除命令

(gdb) i b
Num Type Disp Enb Address What
2 breakpoint keep y 0x00005555555551a7 in main at hello.c:10
stop only if i==2
  • 设置断点状态

基础语法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#设置无效
# disable == dis
# 设置某一个或者某几个断点无效
(gdb) dis 断点1的编号 [断点2的编号 ...]

# 设置某个区间断点无效
(gdb) dis 断点1编号-断点n编号

#设置有效
# enable == ena
# 设置某一个或者某几个断点有效
(gdb) ena 断点1的编号 [断点2的编号 ...]

# 设置某个区间断点有效
(gdb) ena 断点1编号-断点n编号

操作示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
(gdb) i b
Num Type Disp Enb Address What
2 breakpoint keep y 0x00005555555551a7 in main at hello.c:10
stop only if i==2

#设置无效
(gdb) dis 2

(gdb) i b
Num Type Disp Enb Address What
2 breakpoint keep n 0x00005555555551a7 in main at hello.c:10
stop only if i==2

#设置有效
(gdb) ena 2

(gdb) i b
Num Type Disp Enb Address What
2 breakpoint keep y 0x00005555555551a7 in main at hello.c:10
stop only if i==2

调试命令

  • 继续运行gdb
1
2
3
#当采用start命令阻塞在main(),或者设置断点后,阻塞在断点
#c == continue,继续执行
(gdb) c
  • 手动打印信息

    基础语法

    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
    32
    Breakpoint 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) step
    • finish命令

      基本语法:当step进入到函数体内部后,想要跳出函数体,直接使用finsh命令跳出函数体,若函数体内部有断点,需要先将断点删除或者设置为无效,之后再使用finish命令才能正确跳出函数体

    1
    2
    # 如果通过 s 单步调试进入到函数内部, 想要跳出这个函数体
    (gdb) finish
    • next命令

      基本语法:进入断点后,单步调试, 当遇到函数体时,直接跳过函数体,不进入内部。

      1
      2
      3
      # next == n
      # 如果这一行是函数调用, 执行这个命令, 不会进入到函数体的内部
      (gdb) next
    • until命令

      基本语法:在单步调试时,通过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]

感谢丙哥:https://subingwen.cn/linux/gdb/