如何调试段错误 #
段错误是非常常见的一种错误. 主要是内存导致的, 比如数组越界, double free, 解引用空指针等.
假设有一个简单的 C++ 代码如下:
// broken.cpp
int main() {
int *ptr = nullptr;
int value = *ptr;
return 0;
}
编译并运行:
g++ ./broken.cpp -o broken -g
./broken
不出意外的会报段错误:
$ ./broken
Segmentation fault (core dumped)
生成 coredump 文件 #
什么是 coredump 文件
在 Linux 系统中,常将主内存称为核心 (core), 而核心映像 (core image) 就是进程 (process) 执行当时的内存内容. 当进程发生错误或收到信号 (signal) 而终止执行时,系统会将核心映像写入一个文件,以作为调试之用,这就是所谓的核心转储 (core dump).
coredump 文件实际上是一个特殊的 ELF 文件.
首先查看一下允许生成的 core dump 文件的最大大小, 运行 ulimit -a:
$ ulimit -a
real-time non-blocking time (microseconds, -R) unlimited
core file size (blocks, -c) 0
...
可以看到大小是 0, 将其设置为 unlimited:
$ ulimit -c unlimited
对于某些发行版, 这样设置就足够了. 当出现 Segmentation fault 时当前目录会自动生成 coredump 文件.
但对于我用的 Ubuntu 还需要手动打开一下. 查看一下 core_pattern:
$ cat /proc/sys/kernel/core_pattern
|/usr/share/apport/apport -p%p -s%s -c%c -d%d -P%P -u%u -g%g -F%F -- %E
发现会将 core dump 传给 apport. 将其修改为直接写入 core 文件:
$ sudo sysctl -w kernel.core_pattern=core
再次运行 broken 文件就可以得到 coredump.
调试 coredump 文件 #
一般使用 gdb 调试 coredump 文件:
gdb ./broken core
对应的输出:
Core was generated by `./broken'.
Program terminated with signal SIGSEGV, Segmentation fault.
#0 0x000058f79b7a413d in main () at ./broken.cpp:3
3 int value = *ptr;
可以看到定位到了出错在第 3 行. 剩下的就跟正常使用 gdb 调试差不多了.