0%

GDB笔记

GDB(GNU Debugger)是GCC的调试工具。其功能强大,现描述如下:
GDB主要帮忙你完成下面四个方面的功能:

  • 启动你的程序,可以按照你的自定义的要求随心所欲的运行程序。
  • 可让被调试的程序在你所指定的调置的断点处停住。(断点可以是条件表达式)
  • 当程序被停住时,可以检查此时你的程序中所发生的事。
  • 动态的改变你程序的执行环境。

编译生成调试信息

一般来说GDB主要调试的是C/C++的程序。要调试C/C++的程序,首先在编译时,我们必须要把调试信息加到可执行文件中。使用编译器(cc/gcc/g++)的 -g 参数可以做到这一点

1
2
$ gcc -g hello.c -o hello
$ g++ -g hello.cpp -o hello

如果没有-g,你将看不见程序的函数名、变量名,所代替的全是运行时的内存地址。

启动gdb

使用gdb <program>就可以启动gdb用于调试程序。

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
houmin@cosmos:~$ gdb hello
GNU gdb (Ubuntu 7.11.1-0ubuntu1~16.04) 7.11.1
Copyright (C) 2016 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...done.

# 运行程序run,可以简写为r
(gdb) run
Starting program: /home/houmin/hello
hello world
[Inferior 1 (process 3646) exited normally]

# 退出gdb
(gdb) quit
houmin@cosmos:~$

# 设置程序参数
(gdb) set args 10 20
(gdb) show args
Argument list to give program being debugged when it is started is "10 20".

# 指定运行参数,比如 run arg1 arg2

查看信息

栈信息

不管是操作转储文件还是用GDB设置断点进行调试,都可以输入(GDB)bt打印栈内容进行查看。一般的当机BUG,看下当机的位置,然后看下源代码基本就可以解决了。但是很多情况下简单的(GDB)bt还查不到问题,这时候就要涉及到比较复杂的操作。下面罗列了一些对栈的操作:

1
2
3
4
5
6
7
8
9
10
(GDB) bt:显示所有栈帧。
(GDB) bt 10:显示前面10个栈帧。
(GDB) bt -10:显示后面10个栈帧。
(GDB) bt full:显示栈帧以及局部变量。
(GDB) bt full 10:显示前面10个栈帧以及局部变量。
(GDB) bt full -10:显示后面10个栈帧以及局部变量。
(GDB) frame <栈帧编号>:进入指定的栈帧中,然后可以查看当前栈帧中的局部变量,以及栈帧内容等信息。
(GDB) info frame <栈帧编号>:可以查看指定栈帧的详细信息。
(GDB) up:进入上层栈帧。
(GDB) down:进入下层栈帧。

变量

调试BUG过程中查看变量信息是很有帮助的操作,查看方式如下:

1
(GDB) p <变量名>

寄存器

对于调试来说寄存器中的值也很重要,可以查看到当前正在执行的指令的地址等。具体操作罗列如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
(GDB) info reg:显示所有寄存器。可以简写为:i r。如果要查看具体的寄存器可以这样:i $ebx
(GDB) p $eax:显示eax寄存器内容。
(GDB) p/c $eax:用字符显示eax寄存器内容,反斜杠后面的是显示格式

# 可使用的格式见下表:该表在显示内存内容的x命令中也是通用的。
格式 说明
x 显示为十六进制数
d 显示为十进制数
u 显示为无符号十进制数
o 显示为八进制数
t 显示为二进制数
a 显示为地址
c 显示为字符(ASCII)
f 显示为浮点小数
s 显示为字符串
i 显示为机器语言(仅在显示内存的x命令中可用)

内存

可以查看具体内存地址中的内容,比如:目前执行的汇编指令,以及栈中内容等。

1
2
3
4
(GDB) x $pc:显示程序指针指向位置的内容。
(GDB) x/i $pc:显示程序当前位置的汇编指令。
(GDB) x/10i $pc:显示程序当前位置开始往后的10条汇编指令。
(GDB) disassem $pc:反汇编当前函数。简写为:disas $pc

常见命令

1
2
3
x/s 0xbffff890      # Examine a string stored at 0xbffff890
x/20b sum # Examine first 20 opcode bytes of function sum
x/10i sum # Examine first 10 instructions of function sum

调试

断点

设置断点

1
2
3
4
5
6
(GDB) break <函数名>:对当前正在执行的文件中的指定函数设置断点。可简写为:(GDB) b <函数名>
(GDB) break <行号>:对当前正在执行的文件中的特定行设置断点。可简写为:(GDB) b <行号>
(GDB) break <文件名:行号>:对指定文件的指定行设置断点。最常用的设置断点方式。可简写为:(GDB) b <文件名:行号>
(GDB) break <文件名:函数名>:对指定文件的指定函数设置断点。C++类中的方法似乎不好使。可简写为:(GDB) b <文件名:函数名>
(GDB) break <+/-偏移量>:当前指令行+/-偏移量出设置断点。可简写为:b <+/-偏移量>
(GDB) break <*地址>:指定地址处设置断点。可简写为:b <*地址>

查看和删除断点

1
2
3
4
(GDB) info break :显示所有断点以及监视点。可简写为:(GDB) i b
(GDB) delete <编号>:删除编号指向的断点或者监视点。可简写为:(GDB) d <编号>
(GDB) clear <行号>:删除该行的断点。
(GDB) clear <文件名:行号>:删除该行的断点。

设置无效和有效断点

1
2
(GDB) disable <断点编号> : 当前断点设置为无效。
(GDB) enable <断点编号>:当前断点设置为有效。

监视点

可以监视某个变量,在变量被访问或者被修改时程序会在当前点进入断点。删除,查看监视点的方式与断点相同。设置监视点方式如下

1
2
3
(GDB) watch <表达式>:表达式发生变化时暂停。
(GDB) awatch <表达式>:表达式访问或者改变时暂停。
(GDB) rwatch <表达式>:表达式被访问时暂停。

条件断点

在调试程序过程中,有时候我们只想在某个条件下停止程序,然后进行单步调试,而条件断点就是为此而设计。下面是条件断点的操作方式:

1
2
3
(GDB) b <断点> if <条件表达式> : 例如:b main.cpp:8 if x=10 && y=10
(GDB) condition <断点编号>:删除该断点的条件。
(GDB) condition <断点编号> <条件表达式>:修改断点条件。例如:condition 1 x=10 && y=10

断点命令

每次断点发生时候,想要查看的变量很多时,如果每个变量都手动print则需要浪费很多时间。断点命令可以在断点发生时批量执行GDB命令。下面是断点命令的设置方式:

1
2
3
4
(GDB) commands <断点编号>
(GDB) >print x
(GDB) >print y
(GDB) >end

首先输入GDB命令commands <断点编号>然后回车,这时候会出现>提示符。出现>提示符后可以输入断点发生时需要执行的GDB命令,每行一条,全部输入完成后输入end结束断点命令。

反复执行

单步执行时如果进入了你不关心的函数,你想立即跳出函数;或者进入了大循环中,你想立即循环。下面的命令可以帮到你:

1
2
3
4
5
6
(GDB) ignore <断点编号> <次数>:忽略N次断点。
(GDB) c N: 执行N次指令,会忽略断点。
(GDB) s/stepi/n/nexti N:往后执行N行,不会忽略断点。
(GDB) finish:执行完当前函数后停止,不会忽略断点。
(GDB) until:执行完当前循环后停止,不会忽略断点。
(GDB) until <地址>:执行到指定地址停止。

设置变量值

对变量的值进行控制,可以更快的调试自己的程序。下面就是设置变量值的方法:

1
(GDB) set variable <变量> = <表达式>:将变量的值设定为指定表达式的值。例如 set variable x=10

手动生成转储文件:

1
(GDB) generate-core-file 简写为:(GDB) gcore

Reference