• linux backtrace()详细使用说明,分析Segmentation fault【转】


    转自:http://velep.com/archives/1032.html

    在此之前,开发eCos应用程序时,经常碰到程序挂掉后,串口打印输出一大串让人看不懂的数据。今天才明白,原来这些数据是程序挂掉时的堆栈帧数据(stack frame data)。
    
    通过这些堆栈帧数据可以分析出程序当时的运行状态和定位程序哪里出现了问题。
    
    这就是本文要讲的—backtrace()和backtrace_symbols()函数的使用。
    
    backtrace()和backtrace_symbols()函数
    
    backtrace()和backtrace_symbols()函数,包括另一个函数:backtrace_symbols_fd(),它们是GNU对程序调试的扩展支持。
    
    头  文  件
    
    #include <execinfo.h>
    
    函数原型
    
    int backtrace (void **buffer, int size);
    char **backtrace_symbols (void *const *buffer, int size);
    void backtrace_symbols_fd (void *const *buffer, int size, int fd);
    
    函数描述
    
    backtrace()函数,获取函数调用堆栈帧数据,即回溯函数调用列表。数据将放在buffer中。参数size用来指定buffer中可以保存多少个void*元素(表示相应栈帧的地址,一个返回地址)。如果回溯的函数调用大于size,则size个函数调用地址被返回。为了取得全部的函数调用列表,应保证buffer和size足够大。
    
    backtrace_symbols()函数,参数buffer是从backtrace()函数获取的数组指针,size是该数组中的元素个数(backtrace()函数的返回值)。该函数主要功能:将从backtrace()函数获取的地址转为描述这些地址的字符串数组。每个地址的字符串信息包含对应函数的名字、在函数内的十六进制偏移地址、以及实际的返回地址(十六进制)。需注意的是,当前,只有使用elf二进制格式的程序才能获取函数名称和偏移地址,此外,为支持函数名功能,可能需要添加相应的编译链接选项如-rdynamic;否则,只有十六进制的返回地址能被获取。backtrace_symbols()函数返回值是一个字符串指针,是通过malloc函数申请的空间,使用完后,调用者必需把它释放掉。注:如果不能为字符串获取足够的空间,该函数的返回值为NULL。
    
    backtrace_symbols_fd()函数,与backtrace_symbols()函数具有相同的功能,不同的是它不会给调用者返回字符串数组,而是将结果写入文件描述符为fd的文件中,每个函数对应一行。它不会调用malloc函数,因此,它可以应用在函数调用可能失败的情况下。
    
    返  回  值
    
    backtrace()函数返回通过buffer返回的地址个数,这个数目不会超过size。如果这个返回值小于size,那么所有的函数调用列表都被保存;如果等于size,那么函数调用列表可能被截断,此时,一些最开始的函数调用没有被返回。
    
    成功时,backtrace_symbols()函数返回一个由malloc分配的数组;失败时,返回NULL。
    
    注意事项
    
    这些函数对函数返回地址如何保存在栈内有一些假设,注意如下:
    
        忽略帧指针(由gcc任何非零优化级别处理了)可能引起这些假设的混乱。
        内联函数没有栈帧。
        Tail-call(尾调用)优化会导致栈帧被其它调用覆盖。
        为支持函数名功能,可能需要添加相应的编译链接选项如-rdynamic;否则,只有十六进制的返回地址能被获取。
        “static”函数名是不会导出的,也不会出现在函数调用列表里,即使指定了-rdynamic链接选项。
    
    程序用例
    001
        /**
    002
         * rief backtrace测试程序
    003
         *
    004
         * 编译指令:gcc -g -rdynamic backtrace.c -o backtrace
    005
         */
    006
         
    007
        #include <stdio.h>
    008
        #include <stdlib.h>
    009
        #include <unistd.h>
    010
        #include <signal.h>   /* for signal */
    011
        #include <execinfo.h> /* for backtrace() */
    012
         
    013
        #define SIZE    100
    014
         
    015
        void dump(void)
    016
        {
    017
            int j, nptrs;
    018
            void *buffer[100];
    019
            char **strings;
    020
         
    021
            nptrs = backtrace(buffer, SIZE);
    022
            printf("backtrace() returned %d addresses
    ", nptrs);
    023
         
    024
            strings = backtrace_symbols(buffer, nptrs);
    025
            if (strings == NULL) {
    026
                perror("backtrace_symbols");
    027
                exit(EXIT_FAILURE);
    028
            }
    029
         
    030
            for (j = 0; j < nptrs; j++)
    031
                printf("  [%02d] %s
    ", j, strings[j]);
    032
         
    033
            free(strings);
    034
        }
    035
         
    036
        void handler(int signo)
    037
        {
    038
            printf("
    =========>>>catch signal %d (%s) <<<=========
    ",
    039
                        signo, (char *)strsignal(signo));
    040
            printf("Dump stack start...
    ");
    041
            dump();
    042
            printf("Dump stack end...
    ");
    043
         
    044
            /* 恢复并发送信号 */
    045
            signal(signo, SIG_DFL);
    046
            raise(signo);
    047
        }
    048
         
    049
        void printSigno(int signo)
    050
        {
    051
            static int i = 0;
    052
             
    053
            printf("
    =========>>>catch signal %d (%s) i = %d <<<=========
    ",
    054
                        signo, (char *)strsignal(signo), i++);
    055
            printf("Dump stack start...
    ");
    056
            dump();
    057
            printf("Dump stack end...
    ");
    058
        }
    059
         
    060
        void myfunc3(void)
    061
        {
    062
            /* 为SIGINT安装信号处理函数,通过Ctrl + C发出该信号 */
    063
            signal(SIGINT, handler);
    064
            signal(SIGSEGV, handler);   /* 为安装SIGSEGV信号处理函数 */
    065
            signal(SIGUSR1, printSigno);
    066
         
    067
            /* 打印当前函数调用栈 */
    068
            printf("Current function calls list is: 
    ");
    069
            printf("----------------------------------
    ");
    070
            dump();
    071
            printf("----------------------------------
    
    ");
    072
         
    073
            while (1) {
    074
                ;/* 在这里通过Ctrl + C发出一个SIGINT信号来结束程序的运行 */
    075
            }
    076
        }
    077
         
    078
        /**
    079
         * rief
    080
         * 使用static修饰函数,表明不导出这个符号。
    081
         * 即使用-rdynamic选项,看到的只能是个地址。
    082
         */
    083
        static void myfunc2(void)
    084
        {
    085
            myfunc3();
    086
        }
    087
         
    088
        void myfunc(int ncalls)
    089
        {
    090
            if (ncalls > 1)
    091
                myfunc(ncalls -1);
    092
            else
    093
                myfunc2();
    094
        }
    095
         
    096
        int main(int argc, char *argv[])
    097
        {
    098
            if (argc != 2) {
    099
                fprintf(stderr, "%s num-calls
    ", argv[0]);
    100
                exit(EXIT_FAILURE);
    101
            }
    102
             
    103
            myfunc(atoi(argv[1]));
    104
            exit(EXIT_SUCCESS);
    105
        }
    106
         
    107
        // ----------------------------------------------------------------------------
    108
        // End of backtrace.c
    
    编译指令:gcc -g -rdynamic backtrace.c -o backtrace
    
    运行结果:
    
    reille@ubuntu:backtrace$ ./backtrace 3
    Current function calls list is:
    ———————————-
    backtrace() returned 9 addresses
    [00] ./backtrace(dump+0x1f) [0x8048943]
    [01] ./backtrace(myfunc3+0x4b) [0x8048a88]
    [02] ./backtrace [0x8048aa1]
    [03] ./backtrace(myfunc+0x21) [0x8048ac4]
    [04] ./backtrace(myfunc+0x1a) [0x8048abd]
    [05] ./backtrace(myfunc+0x1a) [0x8048abd]
    [06] ./backtrace(main+0x52) [0x8048b18]
    [07] /lib/tls/i686/cmov/libc.so.6(__libc_start_main+0xe6) [0x644b56]
    [08] ./backtrace [0x8048891]
    ———————————-
    
    ^C (按钮Ctrl + C键)
    =========>>>catch signal 2 (Interrupt) <<<=========
    Dump stack start…
    backtrace() returned 10 addresses
    [00] ./backtrace(dump+0x1f) [0x8048943]
    [01] ./backtrace(handler+0x3c) [0x8048a11]
    [02] [0x1f4400]
    [03] ./backtrace [0x8048aa1]
    [04] ./backtrace(myfunc+0x21) [0x8048ac4]
    [05] ./backtrace(myfunc+0x1a) [0x8048abd]
    [06] ./backtrace(myfunc+0x1a) [0x8048abd]
    [07] ./backtrace(main+0x52) [0x8048b18]
    [08] /lib/tls/i686/cmov/libc.so.6(__libc_start_main+0xe6) [0x644b56]
    [09] ./backtrace [0x8048891]
    Dump stack end…
    
    reille@ubuntu:backtrace$
    reille@ubuntu:backtrace$
    
    分析Segmentation fault
    
    对于Segmentation fault错误,有多种分析定位方法,如利用linux产生的core文件、使用gdb进行分析定位。但对于一直运行的程序,特别是大型程序,当意外出现Segmentation fault错误时,其分析定位,则比较棘手。
    
    我们知道,产生Segmentation fault错误时,一般会产生一个SIGSEGV信号。利用这个机制,上述问题传统的做法是,在程序中安装SIGSEGV信号,然后在该信号处理函数中,回溯函数调用列表,从而分析定位错误,一劳永逸。
    
    上面的程序用例中,已安装了SIGSEGV信号。
  • 相关阅读:
    单表清除重复数据
    调用webApi封装
    简单写入本地日志,日志文件位置与主程序exe位置相同
    APPConfig.XML获取配置文件(主程序和Dll各自的)
    获取当前运行程序上一级目录指定文件夹,没有就创建文件夹
    shell脚本中的单引号和双引号以及反引号详解
    Linux shell中反引号(`)的应用
    关于网页 硬解 软解 H264 HEVC 和你电脑起飞了那点事
    浏览器支持H.265解码总结
    微软、谷歌、亚马逊、Facebook等硅谷大厂91个开源软件盘点(附下载地址)
  • 原文地址:https://www.cnblogs.com/sky-heaven/p/9239329.html
Copyright © 2020-2023  润新知