Linux Coredump 核心转储

什么是 Coredump

coredump 核心转储 ,也称为 核心文件(core file) 是操作系统在进程收到某些 信号 而终止运行时,将此进程的地址空间以内容以及有关进程状态的其他信息写出的一个文件。这种信息往往用于调试。
程序员可以通过工具来分析程序运行过程中哪里出错了:Windows 平台用 userdump 和 WinDBG ,Linux 平台使用gdb, elfdump, objdump 等

Windows WinDBG

关于 windbg, 可以参考以下资料

Linux GDB

有些时候进程在crash的时候会产生 core 文件, 但我们却找不到 core 文件,我们需要使用 ulimit 进行一些设置, 这个命令是用来限制系统用户对shell资源的访问的。
ulimit -a 可以查看当前的设置
ulimit -c 可以设置 core 文件的上限,单位为区块(一般 1 block = 512 bytes) .其值为 0 时不写入 core, 为 unlimited 时不限制 core 文件大小。
需要注意, ulimit 只对当前会话有效。若想对所有会话生效, 需要在 /etc/profile 中进行配置。

源文件如下 test_vec.cpp :

1
2
3
4
5
6
7
8
9
#include <iostream>
#include <vector>
using namespace std;

int main(){
std::vector<int> v{42};
cout<<v.at(5)<<endl; //Will crash, and coredump
return 0;
}

编译运行时可能出现如下现象:

1
2
3
4
5
$ g++ -o t_vec ./test_vec.cpp
$ ./t_vec
terminate called after throwing an instance of 'std::out_of_range'
what(): vector::_M_range_check: __n (which is 5) >= this->size() (which is 1)
Aborted (core dumped)

使用 gdb 打开来看:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
$ gdb t_vec core
Core was generated by `./t_vec'.
Program terminated with signal SIGABRT, Aborted.
#0 __GI_raise (sig=sig@entry=6) at ../sysdeps/unix/sysv/linux/raise.c:51
51 ../sysdeps/unix/sysv/linux/raise.c: No such file or directory.
(gdb) i s
#0 __GI_raise (sig=sig@entry=6) at ../sysdeps/unix/sysv/linux/raise.c:51
#1 0x00007fbe77c7f801 in __GI_abort () at abort.c:79
#2 0x00007fbe782d4957 in ?? () from /usr/lib/x86_64-linux-gnu/libstdc++.so.6
#3 0x00007fbe782daab6 in ?? () from /usr/lib/x86_64-linux-gnu/libstdc++.so.6
#4 0x00007fbe782daaf1 in std::terminate() () from /usr/lib/x86_64-linux-gnu/libstdc++.so.6
#5 0x00007fbe782dad24 in __cxa_throw () from /usr/lib/x86_64-linux-gnu/libstdc++.so.6
#6 0x00007fbe782d6855 in ?? () from /usr/lib/x86_64-linux-gnu/libstdc++.so.6
#7 0x000055a4f8cda08a in std::vector<int, std::allocator<int> >::_M_range_check(unsigned long) const ()
#8 0x000055a4f8cd9e9d in std::vector<int, std::allocator<int> >::at(unsigned long) ()
#9 0x000055a4f8cd9c11 in main ()
(gdb)

从 gdb显示的栈信息来看,崩溃发生在 main 函数内的 vector::at 函数内,由 _M_range_check raise 。
如果我们在编译时使用了 -g 选项, 会得到更详细的信息

1
g++ -g3 -o t_vec ./test_vec.cpp

core 文件是一个二进制文件 ,来看一下它的文件信息:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
$ readelf -h core
ELF Header:
Magic: 7f 45 4c 46 02 01 01 00 00 00 00 00 00 00 00 00
Class: ELF64
Data: 2's complement, little endian
Version: 1 (current)
OS/ABI: UNIX - System V
ABI Version: 0
Type: CORE (Core file)
Machine: Advanced Micro Devices X86-64
Version: 0x1
Entry point address: 0x0
Start of program headers: 64 (bytes into file)
Start of section headers: 0 (bytes into file)
Flags: 0x0
Size of this header: 64 (bytes)
Size of program headers: 56 (bytes)
Number of program headers: 32
Size of section headers: 0 (bytes)
Number of section headers: 0
Section header string table index: 0
$ file core
core: ELF 64-bit LSB core file x86-64, version 1 (SYSV), SVR4-style, from './t_vec', real uid: 1000, effective uid: 1000, real gid: 1000, effective gid: 1000, execfn: './t_vec', platform: 'x86_64'

上述信息 Type: CORE (Core file) core: ... core file ... from ... 都表明这是一个 coredump file

信号

上述 core 中有一行信息
Program terminated with signal SIGABRT, Aborted.
表示程序遇到 SIGABRT 信号而终止。信号是由操作系统传给进程的中断,会提早终止一个程序。有些信号不能被程序捕获, 有些可以。系统将一些常用的信号定义在 <csignal> 或 “signal.h” 中:

  • SIGTERM 发送给程序的终止请求

  • SIGSEGV 非法内存访问(分段错误)

  • SIGINT 外部中断,通常为用户所起始

  • SIGILL 非法程序映像,例如非法指令

  • SIGABRT 异常终止条件,例如为 std::abort() 所引发

  • SIGFPE 错误算术运算,例如除以零

这些信号都可能发生 coredump
c++ 提供了 signalraise 捕获和生成信号。

1
2
void (*signal (int sig, void (*func)(int)))(int);
int raise (signal sig);

下面的程序自定义了捕获到 SIGABRT 信号的行为。

1
2
3
4
5
6
7
8
9
10
11
12
#include <iostream>
#include <csignal>
using namespace std;
void signalHandler(int signum){
cout<<"We received a signal "<<signum<<endl;
exit(signum);
}
int main(){
signal(SIGABRT, signalHandler);
abort();
return 0;
}

其它

程序的哪些操作会发生 coredump?

  1. 内存越界访问 OOR

  2. 多线程中使用了线程不安全的函数

  3. 多线程读写的数据未加锁

  4. 非法指针

  5. 堆栈溢出