GDB简单入门
GDB简单入门
GDB简介
GDB
是GNU Project Debugger
简称,是一个调试器。官网介绍说:
(GDB) allows you to see what is going on `inside' another program while it executes -- or what another program was doing at the moment it crashed. 可以看到软件里面发生了什么,或者崩溃时在干什么。
它支持的语言很多,包括:
- Ada
- Assembly
- C
- C++
- D
- Fortran
- Go
- Objective-C
- OpenCL
- Modula-2
- Pascal
- Rust
感觉相当强大,但并不是强大我们就要去使用,而更应该关注在什么场合使用。
当我们在编码环境中,往往IDE
本身会提供调试器以及调试界面(虽然很可能底层用的也是GDB
),此时没有必要去控制台敲gdb ./xxx
,优先使用调试界面即可。
所以使用场合就是:
- 只有一个可执行文件,想去探查一下内部构造;
- 软件跑着崩溃了,想去查一下怎么崩的;
安装、基本使用
- 在
ubuntu
环境中使用apt
安装:sudo apt install gdb
- 在
centos
环境中使用yum
安装:yum install gdb
安装完成后看看是否成功:gdb --version
,安装成功时输出类似下方:
gdb --version
GNU gdb (Ubuntu 9.2-0ubuntu1~20.04.1) 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.
基本用法可以先记住这几个步骤:
- 找到要调试的软件,假设是当前目录下的
./main
可执行文件; - 开始调试:
gdb ./main
,此时进入gdb
的交互控制台,注意main程序此时并没有执行起来; - 输入指令
r
后回车,将软件run
起来; - 遇到崩溃事件时软件运行会暂停,输入指令
bt
后回车,查看调用堆栈(backtrace
); - 搞清楚哪里崩溃的了,
c
指令继续(此时软件将继续执行并退出); - 执行
q
指令退出gdb
交互控制台;
实操示例
- 先编写一个简单的崩溃程序保存为
main.cpp
:
cat > main.cpp <<EOF
#include <iostream>
void test(){
std::cout << "test func" << std::endl;
throw "some error";
}
int main(){
std::cout<<"main started!"<<std::endl;
test();
return 0;
}
EOF
使用
g++
编译输出为main
可执行文件:g++ -o main main.cpp
;进入
gdb
调试交互控制台:gdb ./main
,此时输出如下:
gdb ./main
GNU gdb (Ubuntu 9.2-0ubuntu1~20.04.1) 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 ./main...
(No debugging symbols found in ./main)
(gdb)
- 开始运行
./main
可执行文件:r
回车,可以看到Program received signal SIGABRT, Aborted.
内容,程序崩溃:
(gdb) r
Starting program: /workspace/main
warning: Error disabling address space randomization: Operation not permitted
main started!
test func
terminate called after throwing an instance of 'char const*'
Program received signal SIGABRT, Aborted.
__GI_raise (sig=sig@entry=6) at ../sysdeps/unix/sysv/linux/raise.c:50
50 ../sysdeps/unix/sysv/linux/raise.c: No such file or directory.
(gdb)
- 执行
bt
指令,查看调用堆栈,可以看到从最开始的main()
函数,调用了test()
函数,随后__cxa_throw()
等引发了崩溃:
(gdb) bt
#0 __GI_raise (sig=sig@entry=6) at ../sysdeps/unix/sysv/linux/raise.c:50
#1 0x00007f7c4d709859 in __GI_abort () at abort.c:79
#2 0x00007f7c4d9778d1 in ?? () from /lib/x86_64-linux-gnu/libstdc++.so.6
#3 0x00007f7c4d98337c in ?? () from /lib/x86_64-linux-gnu/libstdc++.so.6
#4 0x00007f7c4d9833e7 in std::terminate() () from /lib/x86_64-linux-gnu/libstdc++.so.6
#5 0x00007f7c4d983699 in __cxa_throw () from /lib/x86_64-linux-gnu/libstdc++.so.6
#6 0x000055606aa9d241 in test() ()
#7 0x000055606aa9d276 in main ()
(gdb)
- 执行
c
指令继续(软件退出),q
指令退出控制台:
(gdb) c
Continuing.
Program terminated with signal SIGABRT, Aborted.
The program no longer exists.
(gdb) q
总结
- 在能使用
IDE
的调试界面时,界面操作会更方便; gdb
是一个控制台程序,就像vim
之类一样;gdb ./main
之后并不会运行,此时输入r
回车,使程序跑起来;- 遇到崩溃事件了软件会暂停,
bt
指令查看调用堆栈;补充一下:如果堆栈很深,可以继续
bt
查看更深的堆栈; c
继续执行,程序崩溃后退出;q
退出gdb
交互控制台。
思考
上面的过程看起来行云流水,但其实并不太实用,试想以下场景:
- 上面
bt
定位到test()
程序了,但仍然不知道具体在哪里,如果我的test()
有几百行是不是就难搞了?
这个问题很好解决,在
g++
编译时添加-g
参数即可:g++ -g -o main main.cpp
,这样在bt
时将会是这样的:#6 0x0000562b8e1ea241 in test () at main.cpp:5 #7 0x0000562b8e1ea276 in main () at main.cpp:10
定位到了
test()
函数中,位于main.cpp
文件的第5
行,非常好用!
并不是所有
bug
都那么老实,会每次都兢兢业业地触发,有可能直接运行的时候都崩溃,轮到使用gdb
了就都不崩(软件和我总得崩一个),当遇到这种概率性bug
时是不是就难搞了?这里就需要引入
core_dump
的相关概念了,下一篇详细再介绍(已经写好了),总的来说可以将程序崩溃时的上下文保存到一个文件(core_file
)中,在gdb
调试时把这个core_file
也带上,就可以复现本次崩溃了。