zoukankan      html  css  js  c++  java
  • 学会用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
    再用gdbbtwhere看就可以了
    (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 添加命令名

    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\n", 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函数时出现的。

  • 相关阅读:
    .Net 平台兼容性分析器
    编程中常见的Foo,是什么意思?
    SoC里住着一只“猫” 网络性能全靠它【转】
    Linux内核:VFIO Mediated Device(vfio-mdev)内核文档(翻译)【转】
    vfio-mdev逻辑空间分析【转】
    29. secure world对smc请求的处理------monitor模式中的处理【转】
    一步步教你:如何用Qemu来模拟ARM系统【转】
    2. [mmc subsystem] mmc core数据结构和宏定义说明【转】
    OP-TEE驱动篇----驱动编译,加载和初始化(一)【转】
    Forkjoin线程池
  • 原文地址:https://www.cnblogs.com/kungfupanda/p/2856885.html
Copyright © 2011-2022 走看看