gdb 笔记(02)— gdb 调试执行(启动调试、添加参数、附加到进程、调试 core 文件)

在编译程序时,使用 gcc 或者 g++ 时一定要加上 -g 选项,如

gcc -g -o hello hello.c

以便调试程序含有调试符号信息,从而能够正常调试程序。否则则会出现如下提示,导致不能调试。

Reading symbols from demo...(no debugging symbols found)...done.

除了不加 -g 选项,也可以使用 Linuxstrip 命令移除掉某个程序中的调试信息,我们这里对 hello_server 使用 strip 命令试试:

[root@localhost testclient]# strip hello_server
##使用 strip 命令之前
-rwxr-xr-x. 1 root root 12416 Sep 8 09:45 hello_server
##使用 strip 命令之后
-rwxr-xr-x. 1 root root 6312 Sep 8 09:55 hello_server

可以发现,对 hello_server 使用 strip 命令之后,这个程序明显变小了(由 12416 个字节减少为 6312 个字节)。我们通常会在程序测试没问题以后,将其发布到生产环境或者正式环境中,因此生成不带调试符号信息的程序,以减小程序体积或提高程序执行效率。

注意:

在实际生成调试程序时,一般不仅要加上 -g 选项,也建议 关闭编译器的程序优化选项。编译器的程序优化选项一般有五个级别,从 O0 ~ O4 ( 注意第一个 O0 ,是字母 O 加上数字 0 ), O0 表示不优化,从 O1 ~ O4 优化级别越来越高,O4 最高。这样做的目的是为了调试的时候,符号文件显示的调试变量等能与源代码完全对应起来。

使用 GDB 调试程序一般有三种方式:

  • gdb filename 直接调试目标程序
  • gdb attach pid 附加进程
  • gdb filename corename 调试 core 文件

1. 直接调试目标程序

假设已有二进制文件如下,其中 chapter_3 为可执行文件

$ ls
chapter_3  main.cpp  Makefile  student.cpp  student.h  teacher.cpp  teacher.h
$ 

执行 gdb 可执行文件名 即可以启动调试,如下图:

$ gdb chapter_3 
GNU gdb (Ubuntu 8.1.1-0ubuntu1) 8.1.1
Copyright (C) 2018 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 chapter_3...done.
(gdb)

如上所示,表示启动调试 chapter_3 成功。这时 gdb 已经成功加载 chapter_3 ,并且可以使用一些 gdb 命令,比如设置断点、查看代码等。执行命令 list 的结果如下所示。

(gdb) list
1	//main.cpp
2	#include <iostream>
3	#include "student.h"
4	#include "teacher.h"
5	int main(int argc,char ** argv)
6	{
7		Student stu("Mike",20);
8		printf("Student's Name is %s,age is %d\n",stu.Name(),stu.Age());
9		Teacher teacher("John",2);
10		printf("Teacher's Name is %s,class number is %d\n",teacher.Name(),teacher.classNumber());
(gdb)

接着需要输入 run 命令或者 r ,程序才会真正的运行起来。

(gdb) r
Starting program: /home/wohu/cppProject/book_debug/chapter_3.1/chapter_3 
Student's Name is Mike,age is 20
Teacher's Name is John,class number is 2
[Inferior 1 (process 24190) exited normally]
(gdb) 

输入 q ,退出 gdb 调试。

(gdb) q
wohu@wohu-dev:~/cppProject/book_debug/chapter_3.1$ 

2. 添加参数

有些程序在启动时需要接收一些参数,如下所示:

#include <iostream>
#include <string>

using namespace std;

int main(int argc, char *argv[])
{
    for (int i = 0; i <= argc; i++)
    {
        cout << "argc[" << i << "] is " << argv[i] << endl;
    }

    return 0;
}

使用 gdb 调试,则需要在 gdb 命令窗口中输入以下命令:

set args a b c d

完整命令如下所示:

wohu@wohu-dev:~/cppProject/book_debug/chapter_3.1$ gdb demo 
....
Reading symbols from demo...(no debugging symbols found)...done.
(gdb) set args a b c d
(gdb) r
Starting program: /home/wohu/cppProject/book_debug/chapter_3.1/demo a b c d
argc[0] is /home/wohu/cppProject/book_debug/chapter_3.1/demo
argc[1] is a
argc[2] is b
argc[3] is c
argc[4] is d
argc[5] is [Inferior 1 (process 24518) exited normally]
(gdb) q
wohu@wohu-dev:~/cppProject/book_debug/chapter_3.1$

可以通过 show args 查看命令行参数是否设置成功。如果单个命令行参数之间含有空格,可以使用引号将参数包裹起来。

(gdb) set args a b c "12  34"
(gdb) show ar
architecture  args
(gdb) show args
Argument list to give program being debugged when it is started is "a b c "12  34"".
(gdb) r
Starting program: /home/weirong/project/cpp/demo a b c "12  34"
argc[0] is /home/weirong/project/cpp/demo
argc[1] is a
argc[2] is b
argc[3] is c
argc[4] is 12  34
argc[5] is [Inferior 1 (process 16433) exited normally]
(gdb)

3. 附加到进程

很多情况下,程序出现问题时并不处于调试状态。也就是说,在我们想要调试程序时,程序已经开始运行,或者服务器代码出现故障时,重启会导致现象丢失,那就只能将 GDB 调试器附加到测试程序上。

如下代码demo.cpp 内容:

#include <iostream>

int main()
{
    int a, b;
    std::cin >> a >> b;
    std::cout << a + b << std::endl;
    return 0;
}

通过 g++ -o demo demo.cpp 生成可执行文件 demo ,执行可执行文件如下:

$ ./demo 

这时我们需要将 gdb 附加到进程的命令如下:

gdb attach pid

这里的 pid 就是我们程序运行的进程 ID ,可以通过命令 ps 来获取。在新会话 Shell 命令行中执行以下命令:

ps aux | grep demo

可以看到目标程序的 pid 为 1361,

$ ps aux | grep demo
wohu      1361  0.0  0.0  13996  1848 pts/0    S+   11:30   0:00 ./demo
wohu      1373  0.0  0.0  16180  1088 pts/1    S+   11:30   0:00 grep --color=auto demo

因此,在 Shell 中执行以下命令

gdb attach 1361

如果是普通用户执行可能报如下错误:

$ gdb attach 1361
...
For help, type "help".
Type "apropos word" to search for commands related to "word"...
attach: No such file or directory.
Attaching to process 1361
Could not attach to process.  If your uid matches the uid of the target
process, check the setting of /proc/sys/kernel/yama/ptrace_scope, or try
again as the root user.  For more details, see /etc/sysctl.d/10-ptrace.conf
ptrace: Operation not permitted.
/home/wohu/cppProject/book_debug/chapter_3.1/1361: No such file or directory.
(gdb) q
$ 

切换 root 账号,再次执行

# gdb attach 1361
...
Type "apropos word" to search for commands related to "word"...
attach: No such file or directory.
Attaching to process 1361
Reading symbols from /home/wohu/cppProject/book_debug/chapter_3.1/demo...(no debugging symbols found)...done.
Reading symbols from /usr/lib/x86_64-linux-gnu/libstdc++.so.6...(no debugging symbols found)...done.
Reading symbols from /lib/x86_64-linux-gnu/libc.so.6...Reading symbols from /usr/lib/debug//lib/x86_64-linux-gnu/libc-2.27.so...done.
done.
Reading symbols from /lib/x86_64-linux-gnu/libm.so.6...Reading symbols from /usr/lib/debug//lib/x86_64-linux-gnu/libm-2.27.so...done.
done.
Reading symbols from /lib64/ld-linux-x86-64.so.2...Reading symbols from /usr/lib/debug//lib/x86_64-linux-gnu/ld-2.27.so...done.
done.
Reading symbols from /lib/x86_64-linux-gnu/libgcc_s.so.1...(no debugging symbols found)...done.
0x00007f4a0928f031 in __GI___libc_read (fd=0, buf=0x55eedbbede70, nbytes=1024)
    at ../sysdeps/unix/sysv/linux/read.c:27
27	../sysdeps/unix/sysv/linux/read.c: No such file or directory.
(gdb) list
22	in ../sysdeps/unix/sysv/linux/read.c
(gdb) set args 3 2
(gdb) r
The program being debugged has been started already.
Start it from the beginning? (y or n) y
Starting program: /home/wohu/cppProject/book_debug/chapter_3.1/demo 3 2

r
32767
[Inferior 1 (process 1624) exited normally]
(gdb) q

当提示 Attaching to process 1361 时就说明我们已经成功地将 GDB 附加到目标进程了。

需要注意的是,程序使用了一些系统库(如 libgcc_s.so),由于这是发行版本的 Linux 系统,这些库是没有调试符号的,因而 GDB 会提示找不到这些库的调试符号。因为目的是调试 ./demo,对系统 API 调用的内部实现并不关注,所以这些提示可以不用关注,只要 ./demo 这个文件有调试信息即可。

当用 gdb attach 上目标进程后,调试器会暂停下来,此时可以在 gdb 中输入相关的命令,比如设置断点等,再继续运行程序,此时需要在 gdb 中输入命令 c 继续运行,程序才能恢复为正常状态。

当调试完程序想结束此次调试时,而且不对当前进程 ./demo 有任何影响,也就是说想让这个程序继续运行,可以在 GDB 的命令行界面输入 detach 命令让程序与 GDB 调试器分离,这样 ./demo 就可以继续运行了:

(gdb) detach
Detaching from program: /home/wohu/cppProject/book_debug/chapter_3.1/demo, process 1361

4. 调试 core 文件

有时候,程序运行一段时间后会突然崩溃,只要程序在崩溃的时候有 core 文件产生,就可以使用这个 core 文件来定位崩溃的原因。当然,Linux 系统默认是不开启程序崩溃产生 core 文件这一机制的,我们可以使用 ulimit -c 命令来查看系统是否开启了这一机制。

wohu@ubuntu:~$ ulimit -a
core file size          (blocks, -c) 0
data seg size           (kbytes, -d) unlimited
scheduling priority             (-e) 0
file size               (blocks, -f) unlimited
pending signals                 (-i) 7856
max locked memory       (kbytes, -l) 64
max memory size         (kbytes, -m) unlimited
open files                      (-n) 65535
pipe size            (512 bytes, -p) 8
POSIX message queues     (bytes, -q) 819200
real-time priority              (-r) 0
stack size              (kbytes, -s) 8192
cpu time               (seconds, -t) unlimited
max user processes              (-u) 7856
virtual memory          (kbytes, -v) unlimited
file locks                      (-x) unlimited
wohu@ubuntu:~$

其中 core file size 那一行默认是 0,表示关闭生成 core 文件,可以使用 ulimit 选项名 设置值 来修改。例如,可以将 core 文件生成改成具体某个值(最大允许的字节数),这里我们使用

ulimit -c unlimited

unlimited-c 选项值)直接修改成不限制大小。

这样修改以后,当我们关闭这个 Linux 会话,设置项的值就会被还原成 0,因此,我们希望这个选项永久生效,永久生效的方式是把 ulimit -c unlimited 这一行加到 /etc/profile 文件中去,放到这个文件最后一行即可。

生成的 core 文件的默认命名方式是 core.pid,举个例子,比如某个程序当时运行时其进程 ID 是 16663,那么它崩溃产生的 core 文件的名称就是 core.16663

通过下面的命令可以调试 core 文件

gdb filename corename

其中,filename 就是程序名,这里就是可执行文件 democorenamecore.16663,我们输入 gdb demo core.16663 来启动调试,然后输入 bt 命令可以看到错误信息。

  • 自定义 core 文件的名称和目录

/proc/sys/kernel/core_uses_pid 可以控制产生的 core 文件的文件名中是否添加 PID 作为扩展,如果添加则文件内容为 1,否则为 0;/proc/sys/kernel/core_pattern 可以设置格式化的 core 文件保存位置或文件名。修改方式如下:

echo "/corefile/core-%e-%p-%t" > /proc/sys/kernel/core_pattern

各个参数的说明如下:

core
假设现在的程序叫 test,我们设置该程序崩溃时的 core 文件名如下:

echo "/root/testcore/core-%e-%p-%t" > /proc/sys/kernel/core_pattern

那么最终会在 /root/testcore/ 目录下生成的 testcore 文件名格式如下:

-rw-------. 1 root root 409600 Jan 14 13:54 core-test-13154-1547445291

需要注意的是,您使用的用户必须对指定 core 文件目录具有写权限,否则生成时会因为权限不足而导致无法生成 core 文件。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

wohu007

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值