1.安装需要使用的工具
sudo apt install gdb build-essential gcc
2.编写测试程序
vi test.c
将下面代码复制到test.c中。
int actual_calc(int a, int b){
int c;
c=a/b;
return 0;
}
int calc(){
int a;
int b;
a=13;
b=0;
actual_calc(a, b);
return 0;
}
int main(){
calc();
return 0;
}
3.执行我们的脚本并配置核心转储
-ggdb 这是一个编译选项,用于在编译过程中生成调试信息。
gcc -ggdb test.c -o test.out
./test.out
显示如下结果说明程序进行了非法的浮点运算,因为上面程序设置的除数为0.
(1)编写shell脚本,对系统核心转储(core dump)相关的配置进行检查和设置。
if ! grep -qi 'kernel.core_pattern' /etc/sysctl.conf; then
sudo sh -c 'echo "kernel.core_pattern=core.%p.%u.%s.%e.%t" >> /etc/sysctl.conf'
sudo sysctl -p
fi
ulimit -c unlimited # -c 专门用于设置核心转储文件的最大大小
#ulimit 命令设置的限制仅对当前 shell 会话有效
grep
是一个用于在文件中查找匹配指定模式的行的工具,在这里它的作用是检查 /etc/sysctl.conf
文件里是否存在 kernel.core_pattern
这一配置项。
-q
选项表示静默模式,即不输出匹配的内容,只返回匹配结果的状态码。
kernel.core_pattern
是内核参数,用于指定核心转储文件的命名模式。
%p
表示产生核心转储的进程的进程 ID。%u
表示产生核心转储的进程的用户 ID。%s
表示导致核心转储的信号编号。%e
表示产生核心转储的进程所对应的可执行文件的文件名。%t
表示核心转储发生的时间戳。
sysctl
是一个用于查看和修改内核参数的工具,-p
选项表示从 /etc/sysctl.conf
文件中加载内核参数配置,使刚刚添加或修改的 kernel.core_pattern
参数生效。
unlimited表示不限制核心转储文件的大小。这样设置后,当程序崩溃产生核心转储时,会生成完整的核心文件,方便后续的调试工作。
ulimit -c #查看当前核心转储文件的大小限制,若为unlimited则设置成功
这段脚本的主要目的是确保 /etc/sysctl.conf
文件中配置了 kernel.core_pattern
参数,若未配置则添加该配置并使其生效,同时设置核心转储文件大小不受限制,以便在程序崩溃时能完整保存核心转储文件,方便后续的调试和分析。
(2)将所有用户的核心转储文件大小限制设置为无限制,并将该设置覆盖写入 /etc/security/limits.conf
文件。
sudo bash -c "cat << EOF > /etc/security/limits.conf
* soft core unlimited
* hard core unlimited
EOF
-c
选项表示让 bash
将后面引号内的字符串作为一条命令来执行。cat
一般用于显示文件内容,这里结合 <<
(Here 文档)使用。EOF是Here 文档的结束标记。* 代表所有用户。
>
是重定向符号,它会把 cat
接收到的 Here 文档内容覆盖写入 /etc/security/limits.conf
文件。如果使用 >>
则是追加写入。
你也可以将 ulimit -c unlimited
添加到你的 shell 配置文件中。这样每次登录都自动应用这个设置。(选一个即可)
gedit ~/.bashrc
source ~/.bashrc
(3)重新执行文件,会看到一个核心文件(具有指定的核心模式)。检查核心转储文件的元数据。
./test.out #设置好核心转储文件后再运行会生成一个core
ls #用ls找到该core
file core.4047.1000.8.test.out.1743061424 #core记得替换为自己生成的
4.使用 GDB 分析核心转储
调用了第一个选项的二进制文件,第二个选项调用了核心文件。
gdb ./test.out ./core.4047.1000.8.test.out.1743061424
我们看到核心转储是由 SIGFPE 生成的,并被告知该信号是一个 SIGFPE,算术异常。
具体错误出现在test.c
文件的第 3 行,执行c = a / b;
时,b
的值为 0,触发了除零错误。
5.回溯
bt
bt(backtrace):如果您使用多个线程,则可以使用该命令来获取在程序崩溃时运行的所有线程的回溯跟踪。
6.框架检查
f:frame
p:print
f 2 #跳转到第二帧
list #列出源代码
p a #打印变量a的值
注意f 0中我们已经定义了变量c,但尚未为其提供初始值。因此,它实际上是未定义的(并且尚未由方程填充,因为该方程失败了),并且结果值可能是从变量分配到的某个地址空间读取的(并且该内存空间可能尚未初始化/清除)。
7.结论
好的我们现在能够调试 C 程序的核心转储,学会了GDB调试基础知识。设置好核心转储(core dump)后,下次再使用GDB进行调试只需要将准备好的c文件编译、运行,ls找到生成的core,然后gdb ./文件 ./核心文件就好了。