使用coredump帮助解决segmentation fault的问题

一、WHAT——什么是coredump

   在执行程序时,有时会遇到“Segmentation fault(core dumped)”,一般说程序core掉了,通常是指应用程序由于异常或bug导致异常退出或终止,并产生一个coredump文件。coredump文件中包含了程序运行时的内存、寄存器状态,堆栈指针、函数调用堆栈等信息,通过对coredump文件的分析,可以帮助我们定位导致程序异常退出的问题并及时解决。

二、WHERE——程序异常退出时,创建的coredump文件放在哪

     默认地,coredump文件跟可执行程序在同一个目录下,可以通过查看core_pattern文件来查看core文件的存储位置

  #cat /proc/sys/kernel/core_pattern 
     core

修改程序对应的coredump文件

echo '/tmp/core-%e-%p-%t' > /proc/sys/kernel/core_pattern

即将coredump文件保存在/tmp目录下,并命名为“core-可执行文件名-PID-timestamp”;

core_patten中使用的变量模板很多,常用的有:

%%:输出一个"%"字符;

%e:executable filename,可执行文件名;

%p:dump进程的PID;

%u:dump进程的UID;

%s:导致该coredump的signal;

%t::coredump文件创建的时间

注:如果第一个字符是'|',表示把后面的pattern作为命令来执行,这样不会创建coredump文件,而是将其输入到某个程序来处理。

比如  '|/usr/share/apport/apport %p %s %c %d %P' 就是表示由apport来处理core dump文件。

三、HOW——开启coredump 

在Ubuntu发行版中,default是不会开启core dump的。

#ulimit -c

检查单个core dump文件的大小,如果返回0,就不会产生coredump文件

#ulimit -c unlimited

对生成的core dump文件大小不做限制;

但是ulimit的作为范围是当前shell进程及其派生出的子进程,如果用户同时运行了两个shell终端进程,只是在其中一个环境中设置了ulimit -c unlimited,那只会在该进程里创建的core dump文件生效,另一个shell终端及其上运行的子进程都不会受其影响。

要想在全部shell窗口生效,需要修改/etc/profile文件,

在/etc/profile中添加: #ulimit -c unlimited

保存后,#source /etc/profile 就会在所有shell窗口中生效了。

四、HOW——如何根据coredump定位程序异常

我们通常使用GDB查看coredump文件,定位程序异常,下面是我使用coredump + GDB下Ubuntu下查找segmentation fault 原因的一个例子。

我在userspace执行一个测试程序pperf时,报告Segmentation fault (core dumped)

查看coredump文件格式

# file /tmp/core-pperf-19395-1584497577

ELF 64-bit LSB core file x86-64;

使用gdb查看coredump文件,定位问题

#gdb /tmp/core-pperf-19395-1584497577

结果出现:not in executable format,file format not recognized问题

使用-c 指定coredump文件即可。

#gdb -c /tmp/core-pperf-19395-1584497577

显示如下:

Core was generated by './pperf 0 0 0 1'

Program terminated with signal SIGSEGV, segmentation fault.

#0 0x0000558fc19c39c6 in ??()

(gdb) list                          //列出源码,显示行号

显示No symbol file is loaded,use the 'file' command

(gdb) file

No executable file now

No symbol file now.

退出gdb,修改Makefile,在CFLAGS中添加-g选项,重新编译程序;这样才能把源文件信息编译到可执行文件中,否则我们就无法在gdb中查看到源程序了。

再次执行pperf程序,出现segmentation fault,保存coredump文件为/tmp/core-pperf-19395-1584497577。之后,在启动gdb时,指定executable file的名字,如下;

#gdb -c /tmp/core-tuxclocker-19395-1584497577 pperf

Program terminated with signal SIGSEGV, segmentation fault.

#0 inner () at cache_miss_load.S:18 

18  wbinvd

(gdb)

能看到这里已经把导致segmentation fault的文件和行号显示出来了,cache_miss_load.S中的18行, wbinvd命令导致了segmentation fault。

去查x86 指令手册,不难发现wbinvd是privileged instruction,需要在CPL=0(即kernel space)下使用,我们这里在userspace下执行会触发#GP(0) 。

 

常用的几个gdb命令

l(list):显示源代码,可以看到对应的行号;

b(break) N:N是行号,表示在对应的行号处设置断点;

p(print) X:X是变量名,表示打印变量X的值;

r(run) :继续执行到断点的位置

n(next):单步执行下一步

c(continue):继续执行;

q(quit):退出gdb;

 

五、WHY——导致coredump的常见原因分析

导致程序coredump的原因有很多,以下列出常见的几种:

1)内存访问越界

    a) 使用错误的下标,导致数组访问越界;

 2)多线程程序使用了线程不安全的函数

3)多线程读写的数据没有加锁保护;

4)非法指针

    a) 使用空指针,

     b) 随意使用指针转换;

5)堆栈溢出

不要使用大的局部变量(因为局部变量都分配在栈上),容易造成堆栈溢出,破坏系统的栈和堆结构,导致莫名其妙的错误。

 

注:Ubuntu下的apport 内部错误报告程序

    Ubuntu的桌面版预装了apport,它是一个错误收集系统,会收集软件崩溃、未处理异常和其他,包括程序bug,并为调试生成崩溃报告。当一个应用程序崩溃或是出现bug 时,Apport就会通过弹窗警告用户并且询问是否提交崩溃报告。

apport service启动之后,默认地使用apport来处理core dump文件,就不会再生成core dump文件了,我们在查看core_patten时,结果如下:

    # cat /proc/sys/kernel/core_pattern 
     |/usr/share/apport/apport %p %s %c %d %P

表示由apport来处理coredump文件;

关闭apport之后,才会创建core dump文件,查看core_pattern如下

    # cat /proc/sys/kernel/core_pattern 
core

临时关闭apport :$sudo service apport stop

重启之后,apport会再次开启;

永久关闭apport,修改/etc/default/apport,设置enabled =0

之后,重启Ubuntu,apport就不会再启动了。 

你可能感兴趣的:(Linux系统)