要想使用 gdb
调试程序,必须让 gdb
程序和被调试程序建立联系,这种联系可以通过程序的可执行文件、core文件或者正在运行的进程来建立,具体调试的时候使用的选项不同,涉及到参数的传递,选项的顺序,多进程启动前的设置等等,接下来可以看一些常见用法。
首先来写一段简单的但是会自动崩溃的代码,主要是为了展示core文件的调试方法,通过调试崩溃产生的core文件是一种很直接的查找问题的方法,可以帮助我们快速定位到问题的栈帧,进而找到具体的逻辑代码。
新建文件 examplepro.cpp,编写代码内容如下:
#include
using namespace std;
int main(int argc, char* argv[])
{
if (argc > 1)
cout << "argv[1] = " << argv[1] << endl;
int a = 3, b = 4;
int c = a + b;
cout << "c = " << c << endl;
int *p = NULL;
*p = c;
return 0;
}
g++ examplepro.cpp -o examplepro -g
albert@home-pc:~/WorkSpace/cpp$ ./examplepro
c = 7
Segmentation fault (core dumped)
我们发现程序在运行之后发生了段错误,这是一种比较常见的BUG,通常由访问无效内存导致,查看程序目录下内容,多了一个叫 core
的文件。
albert@home-pc:~/WorkSpace/cpp$ ls
core examplepro examplepro.cpp
通过这一步你可能看不到这个 core
文件,需要检查两点,第一是编译的时候需要加 -g
选项,第二是使用 ulimit -c unlimited
命令设置core文件占用空间的最小限制,默认大小为0,也就是不产生 core
文件,需要改为 unlimited
才可以,如果你确定产生的 core
文件不会太大,也可以设置一个具体的数值。
有了上面的程序我们就可以进行调试了,因为已经产生了 core 文件,所以先来调试一下 core 文件,看下程序崩溃的原因。
启动程序的语法如下,gdb
命令之后跟程序名,然后后面跟着 core 文件的名字:
gdb examplepro core
具体调试的时候需要换成自己的崩溃的程序名,而core文件大多数是 core.进程id
的形式。
albert@home-pc:~/WorkSpace/cpp$ gdb examplepro core
GNU gdb (Ubuntu 7.11.1-0ubuntu1~16.5) 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 examplepro...done.
[New LWP 19786]
Core was generated by `./examplepro'.
Program terminated with signal SIGSEGV, Segmentation fault.
#0 0x0000000000400932 in main (argc=1, argv=0x7ffd23cc3a18) at examplepro.cpp:15
15 *p = c;
(gdb)
从调试信息来看一下就定位到了问题,在代码的第15行发生了段错误,也就是我们刚刚给野指针赋值的代码。
这种情况就是调试运行,相当于在 gdb
的监控下启动程序,一旦发生错误,gdb
会给出响应的提示,启动方式很简单,gdb
命令之后直接跟着程序名字就可以了。
gdb examplepro
albert@home-pc:~/WorkSpace/cpp$ gdb examplepro
GNU gdb (Ubuntu 7.11.1-0ubuntu1~16.5) 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 examplepro...done.
(gdb) run
Starting program: /home/albert/WorkSpace/cpp/examplepro
c = 7
Program received signal SIGSEGV, Segmentation fault.
0x0000000000400932 in main (argc=1, argv=0x7fffffffdd18) at examplepro.cpp:15
15 *p = c;
(gdb)
这种情况下,启动之后需要输入 run
命令才可以运行程序,这时发现程序又崩溃了。
如果被调试的程序有参数的话,需要将启动的命令进行修改,写成 gdb --args examplepro testparam1
,加上 --args
选项,然后将参数罗列在后面就好了,因为看这些声明很麻烦,我们利用之前学过的 -q
选项来屏蔽启动说明,测试如下:
albert@home-pc:~/WorkSpace/cpp$ gdb -q --args examplepro NB
Reading symbols from examplepro...done.
(gdb) run
Starting program: /home/albert/WorkSpace/cpp/examplepro NB
argv[1] = NB
c = 7
Program received signal SIGSEGV, Segmentation fault.
0x0000000000400932 in main (argc=2, argv=0x7fffffffdd08) at examplepro.cpp:15
15 *p = c;
(gdb)
还有一种写法就是启动 gdb 之后再传参数,具体操作方法如下:
albert@home-pc:~/WorkSpace/cpp$ gdb -q examplepro
Reading symbols from examplepro...done.
(gdb) run NB
Starting program: /home/albert/WorkSpace/cpp/examplepro NB
argv[1] = NB
c = 7
Program received signal SIGSEGV, Segmentation fault.
0x0000000000400932 in main (argc=2, argv=0x7fffffffdd08) at examplepro.cpp:15
15 *p = c;
(gdb)
这种情况是先启动 gdb,然后在执行 run
命令的时候传递参数。
这时需要获得被套是程序的进程id,可以使用 ps
、top
或者 pidof
命令来获取进程id,然后通过 attch
的方式附加到进程。
比如查到需要调试的 examplepro 程序进程号是 3598,那么可以直接启动 gdb
附加到这个进程:
gdb examplepro 3598
也可以先启动 gdb
,然后使用 attach
命令附加到进程:
albert@home-pc:~/WorkSpace/cpp$ gdb -q examplepro
Reading symbols from examplepro...done.
(gdb) attach 3598
如果此时提示进程拒绝被附加通常是权限问题,可以使用所属账号调试,或者可以尝试 sudo
命令。
常见的调试方式就文中提到的这几种,特整理成表格方便对比和查找:
语法 | 解释 |
---|---|
gdb examlepro |
直接 gdb 调试启动 |
gdb examlepro core.3598 |
调试崩溃的 core 文件 |
gdb examlepro 3598 gdb -p 3598 |
附加到正在运行的程序进程上 |
gdb attach 3598 |
先启动gdb,后附加到程序上 |
pidof
命令来直接获取被调试程序的进程号兜兜转转又换了一个住所,匆匆忙忙如蝼蚁般迁徙,路程短了,可选的路却少了。回头看看,一个窝、一段事、一群人而已~
2020-8-25 00:24:01