简介
Linux coredump功能是当Linux下应用程序异常时,Linux内核默认的一种异常信号处理机制,内核会把异常信息与进程内存转储成coredump文件,程序员通过gdb工具可以离线分析应用程序异常时的情况。
使用
文件生成配置
原版coredump不压缩:
- 设置进程的coredump文件大小:ulimit -c unlimited
说明:只对当前进程有效,当前进程的子进程可以继承这一特性。默认为0,不会生成coredump
2. 设置生成coredump文件的路径以及文件名格式:
echo “/dst_dir/core-%e-%t-%s-%p” > /proc/sys/kernel/core_pattern
说明:%e 生成coredump文件的进程名
%t 时间,从1970-01-01 00:00:00开始的秒数
%s 触发coredump的signal号
%p 触发coredump的进程pid
压缩版coredump配置:
coredump文件根据进程使用内存的多少决定生成文件的大小,有时候存储空间有限,就希望能在生成的时候自动压缩一下coredump文件
- 设置进程的coredump文件大小:ulimit -c unlimited
说明:只对当前进程有效,当前进程的子进程可以继承这一特性。默认为0,不会生成coredump
2. 编辑压缩脚本coredump_gzip,存放到/path目录
#!/bin/sh
exec gzip -> /ata4/Log/core-$1-$2-$3-$4.gz
3.设置core_pattern
echo "| /path/coredump_gzip %e %t %s %p" > /proc/sys/kernel/core_pattern
gdb 解析
- 加载coredump文件
gdb 可执行程序 coredump文件
2.进入gdb控制台后,和正常的gdb操作类似
原理相关
1 哪些异常信号会触发coredump
看内核代码吧:
include/linux/signal.h
#define sig_kernel_coredump(sig) siginmask(sig, SIG_KERNEL_COREDUMP_MASK)
#define SIG_KERNEL_COREDUMP_MASK (\
rt_sigmask(SIGQUIT) | rt_sigmask(SIGILL) | \
rt_sigmask(SIGTRAP) | rt_sigmask(SIGABRT) | \
rt_sigmask(SIGFPE) | rt_sigmask(SIGSEGV) | \
rt_sigmask(SIGBUS) | rt_sigmask(SIGSYS) | \
rt_sigmask(SIGXCPU) | rt_sigmask(SIGXFSZ) | \
SIGEMT_MASK )
2 当自定义了信号处理函数后,coredump文件还能生成吗
看内核代码吧:
kernel/signal.c get_signal函数
for (;;) {
…
if (ka->sa.sa_handler != SIG_DFL) {
/* Run the handler. */
ksig->ka = *ka;
if (ka->sa.sa_flags & SA_ONESHOT)
ka->sa.sa_handler = SIG_DFL;
break; /* will return non-zero "signr" value */
}
…
fatal:
if (sig_kernel_coredump(signr)) {
…
do_coredump(&ksig->info);
…
}
}
…
return ksig->sig > 0;
由如上代码可知,当自定义了信号处理函数,不等于SIG_DFL时,代码直接就break循环返回了,不会执行到do_coredump。
但是在一些系统任务严重异常的场景中,如在异常处理过程中重复异常,这种异常的表现一般为进程会退出,这时系统会强制设置信号处理为SIG_DFL,这时仍然会产生coredump
3 coredump文件中记录了哪些内容
看内核代码吧:
Coredump文件的生成在fs/coredump.c文件的do_coredump函数中。
- coredump文件名确定
在format_corename函数中,解析/proc/sys/kernel/core_pattern传入的字符串,确定生成的coredump文件名。其中如果一个字符串是|,则判断为通过管道的形式把生成的文件传到用户态自定义程序处理,如上面的生成压缩格式的coredump文件。
2. 文件生成
(1)文件格式选择
do_coredump函数
…
file_start_write(cprm.file);
core_dumped = binfmt->core_dump(&cprm);
file_end_write(cprm.file);
…
一般linux下程序都为elf格式,所以binfmt->core_dump最终执行的是fs/binfmt_elf.c中的elf_core_dump函数
(2)文件头
elf_core_dump函数:
…
dump_emit(cprm, elf, sizeof(*elf)))
…
(3)notes头
dump_emit(cprm, phdr4note, sizeof(*phdr4note)))
这个段用来记录异常信息
(4)vmaelf_phdr头
进程的每一段vma,记录一个头
dump_emit(cprm, &phdr, sizeof(phdr))
(5)notes头内容填充
write_note_info(&info, cprm)
(6)vmaelf_phdr内容填充
…
for (i = 0, vma = first_vma(current, gate_vma); vma != NULL;
vma = next_vma(vma, gate_vma))
…
page = get_dump_page(addr);
void *kaddr = kmap(page);
stop = !dump_emit(cprm, kaddr, PAGE_SIZE);