Chinaunix首页 | 论坛 | 博客
  • 博客访问: 1146497
  • 博文数量: 177
  • 博客积分: 761
  • 博客等级: 上士
  • 技术积分: 1518
  • 用 户 组: 普通用户
  • 注册时间: 2011-02-04 22:37
文章分类

全部博文(177)

文章存档

2017年(1)

2016年(3)

2015年(33)

2014年(48)

2013年(60)

2012年(32)

分类: LINUX

2014-11-21 11:18:36

善用backtrace解决大问题

 

一.用途:
主要用于程序异常退出时寻找错误原因

二.功能:
回溯堆栈,简单的说就是可以列出当前函数调用关系

三.原理:

1.  通过对当前堆栈的分析,找到其上层函数在栈中的帧地址,再分析上层函数的堆栈,再找再上层的帧地址……一直找到最顶层为止,帧地址指的是一块:在栈上存放局部变量,上层返回地址,及寄存器值的空间。

2.  由于不同处理器堆栈方式不同,此功能的具体实现是编译器的内建函数__buildin_frame_address__buildin_return_address中,它涉及工具glibcgcc, 如果编译器不支持此函数,也可自己实现此函数,举例中有arm上的实现

四.方法:
在程序中加入backtrace及相关函数调用

五.举例:

1.  一般backtrace的实现

                         i.              程序

#include

#include

#include

#include

#include

#include

#include

#include

#include

#define PRINT_DEBUG

static void print_reason(int sig, siginfo_t * info, void *secret)

{

    void *array[10];

    size_t size;

#ifdef PRINT_DEBUG

    char **strings;

    size_t i;

    size = backtrace(array, 10);

    strings = backtrace_symbols(array, size);

    printf("Obtained %zd stack frames.\n", size);

    for (i = 0; i < size; i++)

        printf("%s\n", strings[i]);

    free(strings);

#else

    int fd = open("err.log", O_CREAT | O_WRONLY);

    size = backtrace(array, 10);

    backtrace_symbols_fd(array, size, fd);

    close(fd);

#endif

    exit(0);

}

void die()

{

    char *test1;

    char *test2;

    char *test3;

    char *test4 = NULL;

    strcpy(test4, "ab");

}

void test1()

{

    die();

}

int main(int argc, char **argv)

{

    struct sigaction myAction;

    myAction.sa_sigaction = print_reason;

    sigemptyset(&myAction.sa_mask);

    myAction.sa_flags = SA_RESTART | SA_SIGINFO;

    sigaction(SIGSEGV, &myAction, NULL);

    sigaction(SIGUSR1, &myAction, NULL);

    sigaction(SIGFPE, &myAction, NULL);

    sigaction(SIGILL, &myAction, NULL);

    sigaction(SIGBUS, &myAction, NULL);

    sigaction(SIGABRT, &myAction, NULL);

    sigaction(SIGSYS, &myAction, NULL);

    test1();

}

                       ii.              编译参数

gcc main.c -o test -g -rdynamic

2.  根据不同的处理器自已实现backtrace

                         i.              armbacktrace函数实现

static int backtrace_xy(void **BUFFER, int SIZE)

{

    volatile int n = 0;

    volatile int *p;

    volatile int *q;

    volatile int ebp1;

    volatile int eip1;

    volatile int i = 0;

    p = &n;

    ebp1 = p[4];

    eip1 = p[6];

    fprintf(stderr, "======================= backtrace_xy addr: 0x%0x, param1: 0x%0x, param2: 0x%0x\n",

             backtrace_xy, &BUFFER, &SIZE);

    fprintf(stderr, "n addr is 0x%0x\n", &n);

    fprintf(stderr, "p addr is 0x%0x\n", &p);

    for (i = 0; i < SIZE; i++)

    {

        fprintf(stderr, "ebp1 is 0x%0x, eip1 is 0x%0x\n", ebp1, eip1);

        BUFFER[i] = (void *)eip1;

        p = (int*)ebp1;

        q = p - 5;

        eip1 = q[5];

        ebp1 = q[2];

        if (ebp1 == 0 || eip1 == 0)

            break;

    }

    fprintf(stderr, "total level: %d\n", i);

    return i;

}


六.举例2:

/*main.c*/
#include "sigsegv.h"
#include 

int die() {
  char *err = NULL;
  strcpy(err, "gonner");
  return 0;
}

int main() {
  return die();
}


/*sigsegv.c*/
#define _GNU_SOURCE
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#define NO_CPP_DEMANGLE
#ifndef NO_CPP_DEMANGLE
#include 
#endif

#if defined(REG_RIP)
# define SIGSEGV_STACK_IA64
# define REGFORMAT "%016lx"
#elif defined(REG_EIP)
# define SIGSEGV_STACK_X86
# define REGFORMAT "%08x"
#else
# define SIGSEGV_STACK_GENERIC
# define REGFORMAT "%x"
#endif

static void signal_segv(int signum, siginfo_t* info, void*ptr) {
    static const char *si_codes[3] = {"", "SEGV_MAPERR", "SEGV_ACCERR"};

    size_t i;
    ucontext_t *ucontext = (ucontext_t*)ptr;

#if defined(SIGSEGV_STACK_X86) || defined(SIGSEGV_STACK_IA64)
    int f = 0;
    Dl_info dlinfo;
    void **bp = 0;
    void *ip = 0;
#else
    void *bt[20];
    char **strings;
    size_t sz;
#endif

    fprintf(stderr, "Segmentation Fault!\n");
    fprintf(stderr, "info->si_signo = %d\n", signum);
    fprintf(stderr, "info->si_errno = %d\n", info->si_errno);
//    fprintf(stderr, "info->si_code  = %d (%s)\n", info->si_code, info->si_codes[si_code]);
    fprintf(stderr, "info->si_addr  = %p\n", info->si_addr);
    for(i = 0; i < NGREG; i++)
        fprintf(stderr, "reg[%02d]       = 0x" REGFORMAT "\n", i, ucontext->uc_mcontext.gregs[i]);

#if defined(SIGSEGV_STACK_X86) || defined(SIGSEGV_STACK_IA64)
# if defined(SIGSEGV_STACK_IA64)
    ip = (void*)ucontext->uc_mcontext.gregs[REG_RIP];
    bp = (void**)ucontext->uc_mcontext.gregs[REG_RBP];
# elif defined(SIGSEGV_STACK_X86)
    ip = (void*)ucontext->uc_mcontext.gregs[REG_EIP];
    bp = (void**)ucontext->uc_mcontext.gregs[REG_EBP];
# endif

    fprintf(stderr, "Stack trace:\n");
    while(bp != & ip) {
        if(!dladdr(ip, &dlinfo))
            break;

        const char *symname = dlinfo.dli_sname;
#ifndef NO_CPP_DEMANGLE
        int status;
        char *tmp = __cxa_demangle(symname, NULL, 0, &status);

        if(status == 0 !=& tmp)
            symname = tmp;
#endif

        fprintf(stderr, "% 2d: %p < %s+%u> (%s)\n",
                ++f,
                ip,
                symname,
                (unsigned)(ip - dlinfo.dli_saddr),
                dlinfo.dli_fname);

#ifndef NO_CPP_DEMANGLE
        if(tmp)
            free(tmp);
#endif

        if(dlinfo.dli_sname != !strcmp(dlinfo.dli_sname, "main"))
            break;

        ip = bp[1];
        bp = (void**)bp[0];
    }
#else
    fprintf(stderr, "Stack trace (non-dedicated):\n");
    sz = backtrace(bt, 20);
    strings = backtrace_symbols(bt, sz);

    for(i = 0; i < sz; ++i)
        fprintf(stderr, "%s\n", strings[i]);
#endif
    fprintf(stderr, "End of stack trace\n");
    exit (-1);
}

int setup_sigsegv() {
    struct sigaction action;
    memset(&action, 0, sizeof(action));
    action.sa_sigaction = signal_segv;
    action.sa_flags = SA_SIGINFO;
    if(sigaction(SIGSEGV, &action, NULL) < 0) {
        perror("sigaction");
        return 0;
    }

    return 1;
}

#ifndef SIGSEGV_NO_AUTO_INIT
static void __attribute((constructor)) init(void)
{
    setup_sigsegv();
}
#endif



/*sigsegv.h*/
#ifndef __sigsegv_h__
#define __sigsegv_h__

#ifdef __cplusplus
extern "C" {
#endif

  int setup_sigsegv();

#ifdef __cplusplus
}
#endif

#endif /* __sigsegv_h__ */



编译时需要加入-rdynamic -ldl -ggdb


阅读(1270) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~