GDB 入门--配置
[TOC]
本文介绍 GDB 的安装, 环境配置, 调试的开始与结束等基础操作.
安装
Linux 下
- 安装与否确认
1 | gdb -v |
- 二进制安装
1 | sudo apt -y install gdb |
- 源码安装
下载地址: http://ftp.gnu.org/gnu/gdb/
前提: GCC 编译器
1 | tar -zxvf gdb-9.2.tar.gz |
前期准备: 编译选项
打开
-g
选项
较早以前的 C 语言编译器也允许使用-gg
选项来产生调试信息, 但是现在版本的 GDB 不再支持这种格式产生的调试信息, 所以不建议使用-gg
选项.-Og
优化级别要求- 优化程序介于
O0
~O1
之间. - 在保持快速编译和良好调试体验的同时, 提供较为合理的优化级别.
- 优化程序介于
CMake 构建示例
1 | SET(CMAKE_BUILD_TYPE "Debug") |
调用 GDB
调用方式
直接使用
gdb
命令启动 GDB 调试器
需启动后借助 file 或者 exec-file 命令指定执行程序.调试尚未执行的程序
gdb program
调试正在执行的程序(可能需要 sudo 获取权限).
pidof EXE_FILE_NAME
gdb attach PID
gdb FILENAME PID
gdb -p PID
退出
detach
命令quit
( 或q
)命令
调试执行异常崩溃的程序 core dump
core dump 原因举例: 内存访问越界( 例如数组越界 , 输出字符串时该字符串没有 \0
结束符等 ) , 非法使用空指针等.
通过 GDB 调试产生的 core 文件, 往往可以更快速的定位问题.
ulimit
命令
core 生成开关
ulimit -c
- 若结果为 0, 则表示关闭了此功能, 不会生成 core 文件.
设置 core 文件大小
ulimit -c unlimited/0/1024
- 不限制 core 文件的大小/不生成/上限 1024Kb.
- 如果生成的信息超过此大小, 将会被裁剪, 最终生成一个不完整的 core 文件或者根本就不生成. 如果生成被裁减的 core 文件, 调试此 core 文件的时候, gdb 也会提示错误.
ulimit -a
- 列出所有资源的限制.
- 输出举例:
1
2
3
4
5
6
7
8
9
10
11
12$ ulimit -a
core file size (blocks, -c) 0
data seg size (kbytes, -d) unlimited
file size (blocks, -f) unlimited
max locked memory (kbytes, -l) 4
max memory size (kbytes, -m) unlimited
open files (-n) 2048
pipe size (512 bytes, -p) 8
stack size (kbytes, -s) 10240
cpu time (seconds, -t) unlimited
max user processes (-u) 7168
virtual memory (kbytes, -v) unlimited
ulimit
命令设置后只对一个终端有效, 所以另起终端后需要重新设置.
设置 core 文件的名称和文件路径
默认生成路径: 输入可执行文件运行命令的同一路径下.
默认生成名字: 默认命名为 core. 新的 core 文件会覆盖旧的 core 文件.
例如设置 PID 作为文件扩展名:/proc/sys/kernel/core_uses_pid
可以控制 core 文件的文件名中是否添加 PID 作为扩展.
文件内容为 1, 表示添加 PID 作为扩展名, 生成的 core 文件格式为 core.xxxx. 为 0 则表示生成的 core 文件同一命名为 core.
可通过以下命令修改此文件:
1
echo "1" > /proc/sys/kernel/core_uses_pid
/proc/sys/kernel/core_pattern
或者/etc/sysctl.conf
文件, 可以控制 core 文件保存位置和文件名格式. 到底哪一个和系统版本相关, 需要实际测试.使用 vi 可能无法成功编辑
proc/sys/kernel/core_pattern
, 只能使用echo
命令修改或者命令sysctl
修改.1
2
3echo "/corefile/core-%e-%p-%t" > /proc/sys/kernel/core_pattern
#OR
sysctl -w kernel.core_pattern=/corefile/core.%e.%p.%s.%E参数列表
%p
- insert pid into filename 添加pid(进程id).%u
- insert current uid into filename 添加当前uid(用户id).%g
- insert current gid into filename 添加当前gid(用户组id).%s
- insert signal that caused the coredump into the filename 添加导致产生core的信号.%t
- insert UNIX time that the core dump occurred into filename 添加core文件生成时的unix时间.%h
- insert host name where the core dump happened into filename 添加主机名.%e
- insert coredumping executable name into filename 添加导致产生core的命令名.
修改
/etc/sysctl.conf
- 添加需要保存的路径
kernel.core_pattern = /tmp/corefile/core.%e.%t
, 需要注意的是该路径必须应用有写的权限, 不然 core 文件是不会生成的. 再执行命令sysctl -p
即可生效. - 需要注意的是, 如果
/tmp/corefile
目录原先不存在, 那么生成的 core dump 文件就无处存放, 所以要先确定设置的目录是事先存在的.
- 添加需要保存的路径
快速验证配置是否生效
用命令:kill -s SIGSEGV $$
验证是否能生成 core 文件.
查看问题
gdb 程序名 core 名.
例子 问题源码:
1 |
|
报错:
1 | $ gcc -Wall -g foo.c |
设置 core dump
1 | $ ulimit -c 1024 |
查看结果
1 | $ gdb --core=core.9128 |
此时用 bt
看不到 backtrace, 也就是调用堆栈, 原来 GDB 还不知道符号信息在哪里. 我们告诉它一下:
1 | (gdb) file ./a.out |
启动参数
启动参数 | 功能 |
---|---|
-pid -p |
调试特定进程的程序 |
-symbols file -s file |
仅从指定 file 文件中读取符号表 |
-quient -q -silent |
取消启动 GDB 调试器时打印的介绍信息和版权信息 |
-cd directory |
以 directory 作为启动 GDB 调试器的工作目录, 而非当前所在目录 |
-args args1 args2 |
向可执行文件传递执行所需要的参数 |
-x file_name |
从指定的文件读取断点设置等命令 |
gdb 的长选项可以是 -
, 或者 --
. 一般选择较短的 -
.
1 | $ gdb -help |
运行环境设置
file
命令
指定要调试的目标程序. 可以在运行 gdb 过后指定要调试的程序.
传递被调试程序参数
用 argc
和 argv[]
接收参数.
启动时,
-args
选项1
$ gdb -args ./a.out a b c
启动后,
set args
命令1
2
3(gdb) set args a b c
(gdb) show args
Argument list to give program being debugged when it is started is "a b c".运行程序时
1
2
3
4
5
6(gdb) r a b
Starting program: /home/xmj/tmp/a.out a b
(gdb) show args
Argument list to give program being debugged when it is started is "a b".
(gdb) r
Starting program: /home/xmj/tmp/a.out a b可以看出, 参数已经被保存了, 下次运行时直接运行
run
命令, 即可.让已经设置的参数为空
1
(gdb) set args
设置环境变量
- 临时设置
PATH
环境变量
1 | (gdb) path /temp/demo |
- 设置被调试程序的环境变量:
set env varname=value
1 | set env LD_PRELOAD=/lib/x86_64-linux-gnu/libpthread.so.0 |
指定当前工作目录
- 在 gdb 中直接执行
cd
和pwd
命令
1 | (gdb) pwd |
重定向
GDB 调试器允许对执行程序的输入和输出进行重定向, 使其从文件或其它终端接收输入, 或者将执行结果输出到文件或其它终端.
1 | (gdb) run > a.txt |
help
命令
help
命令不加任何参数会得到命令的分类
1 | (gdb) help |
help class
: class 类别下所有命令的列表和命令功能.help command
命令得到某一个具体命令的用法.- 用
apropos regexp
命令查找所有符合regexp
正则表达式的命令信息.1
2
3
4
5
6
7(gdb) apropos set
awatch -- Set a watchpoint for an expression
b -- Set breakpoint at specified line or function
br -- Set breakpoint at specified line or function
bre -- Set breakpoint at specified line or function
brea -- Set breakpoint at specified line or function
......
启动时不显示提示信息
启动时 -q
选项.
长期设置: 在 ~/.bashrc
中, 为 gdb 设置一个别名: alias gdb="gdb -q"
.
退出时不显示提示信息
1 | A debugging session is active. |
使用 (gdb) set confirm off
.
也可以把这个命令加到 .gdbinit
配置文件里, 不用每次输入.
输出信息多时不会暂停输出
当输出信息较多时, gdb 会暂停输出, 并会打印 ---Type <return> to continue, or q <return> to quit---
这样的提示信息, 如下面所示:
1 | 81 process 2639102 0xff04af84 in __lwp_park () from /usr/lib/libc.so.1 |
使用 set pagination off
或者 set height 0
命令. 这样 gdb 就会全部输出, 中间不会暂停.
设置命令提示符
当存在多个 GDB 版本时可以通过提示符进行区分.
查看当前 GDB 版本
1 | $ gdb -q `which gdb` |
使用 set prompt (main gdb)
来区分到底是在用哪个版本的 gdb.
设置源文件查找路径
有时在运行状态下, gdb 不能准确地定位到源文件的位置(比如文件被移走了, 等等), 此时可以用 directory PATH
(dir PATH
) 命令设置查找源文件的路径.
如果希望在 gdb 启动时, 加载 code 的位置, 避免每次在 gdb 中再次输入命令, 可以使用 gdb 的 -d
参数.
1 | gdb a.out -d /search/code/some |
有时调试程序时, 源代码文件可能已经移到其它的文件夹了. 此时可以用 set substitute-path from PATH1 to PATH2
命令设置新的文件夹( PATH2 )目录替换旧的(PATH1).
支持预处理器宏信息
使用 gcc -g
编译生成的程序, 是不包含预处理器宏信息的.
例子代码
1 |
|
1 | (gdb) p NAME |
使用 gcc -g3
进行编译可以获得预处理的宏信息:
1 | (gdb) p NAME |
在 gdb 中执行 shell 命令和 make
1 | (gdb) shell ls |
!
和命令之间不需要有空格.
当在构建环境(build 目录)下调试程序的时候, 可以直接运行 make.
1 | (gdb) make CFLAGS="-g -O0" |
指定程序的输入输出设备
在 gdb 中, 默认下程序的输入输出是和 gdb 使用同一个终端. 也可以为程序指定一个单独的输入输出终端.
- 首先, 打开一个新终端, 使用如下命令获得设备文件名:
1
2$ tty
/dev/pts/2 - 然后, 通过命令行选项指定程序的输入输出设备:
1
2$ gdb -tty /dev/pts/2 ./a.out
(gdb) r - 或者, 在 gdb 中, 使用命令进行设置:
1
(gdb) tty /dev/pts/2
开始与结束
run
( r
)与 start
命令
- 例子
1
2
3
4
5
6
7
8
9
10
11
12
13
14//存储路径为 /tmp/demo/main.c
//其生成的可执行文件为 main.exe, 位于同一路径下
int main(int argc,char* argv[])
{
FILE * fp;
if((fp = fopen(argv[1],"r")) == NULL){
printf("file open fail");
}
else{
printf("file open true");
}
return 0;
}
调试过程
1 | # pwd <--显示当前工作路径 |
默认情况下, run
命令会一直执行程序, 直到执行结束. 如果程序中手动设置有断点, 则 run
命令会执行程序至第一个断点处.start
命令会执行程序至 main() 主函数的起始位置, 即在 main() 函数的第一行语句处停止执行( 该行代码尚未执行 ).
使用 start
命令启动程序, 等价于先在 main() 主函数起始位置设置一个断点, 然后再使用 run
命令启动程序.
程序执行过程中使用 run
或者 start
命令, 表示的是重新启动程序.
finish
和 return
命令
finish
命令会执行函数到正常退出.return
命令是立即结束执行当前函数并返回.return
命令还有一个功能, 即可以指定该函数的返回值.
资源推荐
cheat sheet: https://users.ece.utexas.edu/~adnan/gdb-refcard.pdf