学会用core dump调试程序错误

什么是coredump?

通常情况下coredmp包含了程序运行时的内存,寄存器状态,堆栈指针,内存管理信息等。可以理解为把程序工作的当前状态存储成一个文件。许多程序和操作系统出错时会自动生成一个core文件。

如何使用coredump?

coredump可以用在很多场合,使用Linux,或者solaris的人可能都有过这种经历,系统在跑一些压力测试或者系统负载一大的话,系统就hang住了或者干脆system panic.这时唯一能帮助你分析和解决问题的就是coredump了。

现在很多应该程序出错时也会出现coredump.

分析coredump的工具

现在大部分类unix操作系统都提供了分析core文件的工具,比如 GNU Binutils Binary File Descriptor library (BFD), GNU Debugger (gdb),mdb等

coredump的文件格式

类unix操作系统中使用efi格式保存coredump文件。

在solairs下

bash-3.2# file *unix.3 ELF 32-bit LSB executable 80386 Version 1, statically linked, not stripped, no debugging information availableunix.4 ELF 32-bit LSB executable 80386 Version 1, statically linked, not stripped, no debugging information available

造成程序coredump的原因很多,这里根据以往的经验总结一下:

1 内存访问越界   a) 由于使用错误的下标,导致数组访问越界   b) 搜索字符串时,依靠字符串结束符来判断字符串是否结束,但是字符串没有正常的使用结束符
  c) 使用strcpy, strcat, sprintf, strcmp, strcasecmp等字符串操作函数,将目标字符串读/写爆。应该使用strncpy, strlcpy, strncat, strlcat, snprintf, strncmp, strncasecmp等函数防止读写越界。   2 多线程程序使用了线程不安全的函数。 应该使用下面这些可重入的函数,尤其注意红色标示出来的函数,它们很容易被用错:
asctime_r(3c) gethostbyname_r(3n) getservbyname_r(3n) ctermid_r(3s) gethostent_r(3n) getservbyport_r(3n) ctime_r(3c) getlogin_r(3c) getservent_r(3n) fgetgrent_r(3c) getnetbyaddr_r(3n) getspent_r(3c) fgetpwent_r(3c) getnetbyname_r(3n) getspnam_r(3c) fgetspent_r(3c) getnetent_r(3n) gmtime_r(3c) gamma_r(3m) getnetgrent_r(3n) lgamma_r(3m) getauclassent_r(3) getprotobyname_r(3n) localtime_r(3c) getauclassnam_r(3) etprotobynumber_r(3n) nis_sperror_r(3n) getauevent_r(3) getprotoent_r(3n) rand_r(3c) getauevnam_r(3) getpwent_r(3c) readdir_r(3c) getauevnum_r(3) getpwnam_r(3c) strtok_r(3c) getgrent_r(3c) getpwuid_r(3c) tmpnam_r(3s) getgrgid_r(3c) getrpcbyname_r(3n) ttyname_r(3c) getgrnam_r(3c) getrpcbynumber_r(3n) gethostbyaddr_r(3n) getrpcent_r(3n)   3 多线程读写的数据未加锁保护。 对于会被多个线程同时访问的全局数据,应该注意加锁保护,否则很容易造成core dump   4 非法指针   a) 使用空指针
  b) 随意使用指针转换。一个指向一段内存的指针,除非确定这段内存原先就分配为某种结构或类型,或者这种结构或类型的数组,否则不要将它转换为这种结构或类型 的指针,而应该将这段内存拷贝到一个这种结构或类型中,再访问这个结构或类型。这是因为如果这段内存的开始地址不是按照这种结构或类型对齐的,那么访问它 时就很容易因为bus error而core dump.   5 堆栈溢出 不要使用大的局部变量(因为局部变量都分配在栈上),这样容易造成堆栈溢出,破坏系统的栈和堆结构,导致出现莫名其妙的错误。

coredump文件的生成方法以及使用方法:

(假设下例是在x86上交叉编译,而在arm上运行异常的现象)

1.  在arm内核里加入coredump的支持(一般内核都支持coredump, 不用重编)

2.  运行命令,此时允许coredump文件产生:(在arm上)  ulimit –c unlimited

3.  执行程序:(在arm上)
./test 在异常退出时,会显示如下信息,注意括号里的内容Segmentation fault (core dumped)程序执行目录下将产生*core文件

4.  用gdb分析:(在x86上)
arm-linux-gdb ./test test.core 再用gdb的bt或where看就可以了
(arm-linux-gdb的编译见<调试工具之四gdbserve>)

系统支持生成core并设置存储位置的方法:

1> 在/etc/profile中加入以下一行,这将允许生成coredump文件 ulimit -c unlimited
2> 在rc.local中加入以下一行,这将使程序崩溃时生成的coredump文件位于/tmp目录下: echo /tmp/core.%e.%p > /proc/sys/kernel/core_pattern  
/tmp/也可以是其它的目录位置。最佳位置应当满足以下需求: * 对所有用户可写 * 空间容量足够大 * 掉电后文件不丢失

------

最来在项目中遇到大型程序出现SIGSEGV ,一直不知道用core dump工具来调试程序,花了近一周的时间,才定位问题,老大很生气,后果很严重,呵呵,事后仔细学习了这块的知识,了解一点core dump的知识。

      在使用半导体作为内存的材料前,人类是利用线圈当作内存的材料(发明者为王安),线圈就叫作core ,用线圈做的内存就叫作“core memory”。(线圈的单词应该是coil,呵呵)如今,半导体工业澎勃发展,已经没有人用线圈当内存了,不过,在许多情况下,人们还是把内存叫作“core”。 所以注意了:这里的core不是核心,而是内存。不过结合实际来看,好像也有点“内核所占内存”的意思。       core dump又是什么东东?   我 们在开发(或使用)一个程序时,最怕的就是程序莫明其妙地挂掉。虽然系统没事,但我们下次仍可能遇到相同的问题。于是,这时操作系统就会把程序挂掉时的 内存内容写入一个叫做core的文件里(这个写入的动作就叫dump,dump的英语意思是垃圾、倾倒。从这里来看,这些内存的内容是程序错误运行的结 果,所以算是垃圾,把他弄出来就好比从大的内存池里“倾倒”。),以便于我们调试。这个过程,因此叫做core dump。

1. 在嵌入式系统中,有时core dump直接从串口打印出来,结合objdump查找ra和epa地址,运用栈回溯,可以找到程序出错的地方。

2. 在一般Linux系统中,默认是不会产生core dump文件的,通过ulimit -c来查看core dump文件的大小,一般开始是0,可以设置core文件大小,ulimit -c 1024(kbytes单位)或者ulimit -c unlimited。

3. core dump文件输出设置,一般默认是当前目录,可以在/proc/sys/kernel中找到core-user-pid,通过

echo "1" > /proc/sys/kernel/core-user-pid使core文件名加上pid号,还可以用

mkdir -p /root/corefile

echo "/root/corefile/core-%e-%p-%t" > /proc/sys/kernel/core-pattern控制core文件保存位置和文件名格式。

以下是参数列表:
    %p - insert pid into filename 添加pid
    %u - insert current uid into filename 添加当前uid
    %g - insert current gid into filename 添加当前gid
    %s - insert signal that caused the coredump into the filename 添加导致产生core的信号
    %t - insert UNIX time that the coredump occurred into filename 添加core文件生成时的unix时间
    %h - insert hostname where the coredump happened into filename 添加主机名
    %e - insert coredumping executable name into filename 添加文件名

%c 转储文件的大小上限
%e 所dump的文件名
%g 所dump的进程的实际组ID
%h 主机名
%p 所dump的进程PID
%s 导致本次coredump的信号
%t 转储时刻(由1970年1月1日起计的秒数)
%u 所dump进程的实际用户ID

  

4. 用gdb查看core文件:
下面我们可以在发生运行时信号引起的错误时发生core dump了.编译时加上-g
发生core dump之后, 用gdb进行查看core文件的内容, 以定位文件中引发core dump的行.
gdb [exec file] [core file]
如:
gdb ./test test.core
在进入gdb后, 用bt命令查看backtrace以检查发生程序运行到哪里, 来定位core dump的文件行.

5. 给个例子

test.c

void a()

{

   char *p = NULL;

   printf("%d/n", *p);

}

int main()

{

    a();

    return 0;

}

编译 gcc -g -o test test.c

运行 ./test

报segmentation fault(core dump)

gdb ./test test.core如果生成的是test.core.

http://blog.csdn.net/wen0006/article/details/3945845

--------

一,什么是core文件?什么是core dump?为什么要提出这?

 

 

Gdb手册上是这么描述core文件的定义的,A core file or core dump is a file 

that records the memory image of a running process and its process 

status(register values etc...)。

 

 

Core文件理解:当一个进程崩溃时,在该进程会在指定目录生成一个core文件

用来记录该进程崩溃时的内存映像,并附带了一些调试信息。该文件主要供

调试使用。

 

 

  (1)在软件开发中,很多bug只能在特定的环境和条件下才能出现,重现的时机

和条件都很难把握。有可能出现很多次测试后该问题才重现一次。这就给调试带

来了很多不便。

  (2)还有一种情形,就是软件的代码量比较大,如果靠设断点和单步跟踪也很

麻烦,起码很费时间。

    为了解决上面的两个问题,就提出了core dump技术,在程序崩溃的时候,将

程序的内存映像保存到一个core文件中去,然后通过分析这个core文件来找到程序

崩溃的原因。 ----------------------------------------------------------------

 

二,怎么查看系统是否打开生成core文件的功能?没有开启又该如何开启?

 

 

可以使用ulimit命令查看。

#ulimit -a

core file size (blocks, -c) 0

data seg size (kbytes, -d) unlimited

file size (blocks, -f) unlimited

max locked memory (kbytes, -l) 4

max memory size (kbytes, -m) unlimited

open files (-n) 2048

pipe size (512 bytes, -p) 8

stack size (kbytes, -s) 10240

cpu time (seconds, -t) unlimited

max user processes (-u) 7168

virtual memory (kbytes, -v) unlimited

 

 

core file size (blocks, -c) 0说明core文件的最大大小为0,说明core dump

没有开启。可以使用ulimit -c unlimited来开启core dump。

---------------------------------------------------------------

 

三,如何设置core dump文件名及其文件位置?

 

 

/proc/sys/kernel/core_uses_pid 这个文件内容为0的话,所有core dump文件

名都是core,没有扩展名;内容为1的话,文件名编程core.pid,即加上进程号作

为扩展名。

 

 

  sudo echo "1" > /proc/sys/kernel/core_uses_pid 修改该参数。

 

 

 

还可以通过修改/proc/sys/kernel/core_pattern来指定core dump文件位置和文件名,

此文件内容改为/tmp/coredumpfile/core-%e-%p-%t,表示所有的core dump都放在/tmp/coredumpfile目录下,文件名为core-命令名-pid-系统时间详细参数列表

 

%p - insert pid into filename 添加pid

%u - insert current uid into filename 添加当前uid

%g - insert current gid into filename 添加当前gid

%s - insert signal that caused the coredump into the filename 添加导致产生core的信号

%t - insert UNIX time that the coredump occurred into filename 添加core文件生成时的unix时间

%h - insert hostname where the coredump happened into filename 添加主机名

%e - insert coredumping executable name into filename 添加命令名
----------------------------------------------------------------------------

四,如何使用gdb来调试core文件?

 

 

gdb [选项] 被调试的可执行文件 core文件

 

执行该命令,gdb会产生生成该core文件的进程名,中断该进程的信号等。

 

 

     当进程接收到一下UNIX信号时会产生core文件。

 

    SIGABRT :调用abort函数产生该信号,进程异常终止。

    SIGFPE:该信号表示一个算术运算异常,例如除以0,浮点数溢出等。

    ...

-----------------------------------------------------------------------------

 

五,实例:(通过除数为0制造出core dump)

 

  1. #include <stdio.h>
  2. int wib(int no1, int no2)
  3. {
  4.   int result, diff;
  5.   diff = no1 - no2;
  6.   result = no1 / diff;
  7.   return result;
  8. }
  9. int main(int argc, char *argv[])
  10. {
  11.   int value, div, result, i, total;
  12.   value = 10;
  13.   div = 6;
  14.   total = 0;
  15.   for(i = 0; i < 10; i++)
  16.   {
  17.     result = wib(value, div);
  18.     total += result;
  19.     div++;
  20.     value--;
  21.   }
  22.   
  23.   printf("%d wibed by %d equals %d ", value, div, total);
  24.   return 0;
  25. }
  1. root@linux:/home/linux/dir# gdb -q a.out core.2075
  2. Reading symbols from /home/linux/dir/a.out...(no debugging symbols found)...done.
  3. [New LWP 2075]
  4. Core was generated by `./a.out'.
  5. Program terminated with signal 8, Arithmetic exception.
  6. #0  0x080483e1 in wib ()
  7. (gdb) lbt
  8. Undefined command: "lbt".  Try "help".
  9. (gdb) bt
  10. #0  0x080483e1 in wib ()
  11. #1  0x0804842b in main ()
  12. (gdb)
通过调试core文件,可以很清楚地看到。是由signal 8这信号将进程中断了,

中断的原因是 Arithmetic exception

 

而且通过bt指令可以看到出现异常的函数是在执行wib函数时出现的。

What Doesn't Kill Me Makes Me Stronger
原文地址:https://www.cnblogs.com/fanblogs/p/12654552.html