欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

崩溃时打印堆栈调用日志

程序员文章站 2023-01-13 10:07:37
在gnu/linux中,我们可能会遇到程序因为内存访问错误而崩溃 或类似的问题。一般情况下,我们借助程序崩溃后生成的core文件 来定位引起程序崩溃的位置。 但有时我们无法在...

在gnu/linux中,我们可能会遇到程序因为内存访问错误而崩溃
或类似的问题。一般情况下,我们借助程序崩溃后生成的core文件
来定位引起程序崩溃的位置。
但有时我们无法在现场调试,只能依靠用户传回的一些日志文件的
内容来定位程序错误的位置。如果这时日志中包含程序崩溃时的栈
调用信息,那么对排除错误将会有些帮助。

使用backtrace函数和addr2line程序可以帮助我们实现这个愿望!
文章最后的代码是一个简陋的实现。

编译:
        g++ -g -rdynamic -o mytest main.cpp

        注意: 不加 -g, addr2line程序无法打印行号;
              不加 -dynamic, backtrace无法打印完整信息,
              可能会没有和函数名称相关的信息;

执行:
        ./mytest

不过程序总有bug,backtrace和addr2line也一样,
不知为何行号都向下窜了一行?我用c语言程序试的时候
没这个问题;
对于某些程序,可能无法打印出完整堆栈信息,这时改用core
方法,也无法得到完整堆栈信息。
程序在fedora11上打印出如下信息,注意那个static函数。
=================================
frame info:
./mytest(_z10dump_stackp8_io_file+0x1f) [0x8048abb]
./mytest(_z11segv_handlei+0x13) [0x8048cba]
[0x709400]
/lib/libc.so.6(memcpy+0x5a) [0x1c22ba]
./mytest(_z5foo_1i+0x3e) [0x8048a95]
./mytest [0x8048a55]
./mytest(_z5foo_1i+0x1a) [0x8048a71]
./mytest [0x8048a55]
./mytest(_z5foo_1i+0x1a) [0x8048a71]
./mytest [0x8048a55]
./mytest(_z5foo_1i+0x1a) [0x8048a71]
./mytest [0x8048a55]
./mytest(main+0x86) [0x8048a3a]
/lib/libc.so.6(__libc_start_main+0xe6) [0x15da66]
./mytest [0x8048921]
src info:
dump_stack(_io_file*)
/home/shuheng/temp_blog/main.cpp:59
segv_handle(int)
/home/shuheng/temp_blog/main.cpp:99
??
??:0
??
??:0
foo_1(int)
/home/shuheng/temp_blog/main.cpp:53
foo
/home/shuheng/temp_blog/main.cpp:44
foo_1(int)
/home/shuheng/temp_blog/main.cpp:48
foo
/home/shuheng/temp_blog/main.cpp:44
foo_1(int)
/home/shuheng/temp_blog/main.cpp:48
foo
/home/shuheng/temp_blog/main.cpp:44
foo_1(int)
/home/shuheng/temp_blog/main.cpp:48
foo
/home/shuheng/temp_blog/main.cpp:44
main
/home/shuheng/temp_blog/main.cpp:38
??
??:0
_start
??:0
=================================

main.cpp:
========================================
// 2012年 02月 06日 星期一 09:20:08 cst
// author: 李小丹(li shao dan) 字 殊恒(shuheng)
// k.i.s.s
// s.p.o.t
// copy from my tst_execinfo.c

// xxx g++ -g -rdynamic -o mytest main.cpp
// man backtrace_symbols
// man addr2line


#include <cstdio>
#include <cstdlib>
#include <csignal>
#include <cstring>

#include <unistd.h>
#include <execinfo.h>


void dump_stack(file *);
void segv_handle(int);

static int foo(int);
int foo_1(int);

int main()
{
    struct sigaction sa;
    memset(&sa, 0, sizeof(sa));
    sa.sa_handler = segv_handle;
    sigemptyset(&sa.sa_mask);
    if(sigaction(sigsegv, &sa, 0) < 0) {
        perror("sigaction");
        exit(1);
    }
    foo(7);
    return 0;
}

static int foo(int a)
{
    return foo_1(a - 1);
}

int foo_1(int a)
{
    if(a > 0) return foo(a - 1);

    char *p = 0;
    //crash
    memcpy(p, "hello", 5);
    return 0;
}

void dump_stack(file *log)
{
    void *bufs[100];
    int n = backtrace(bufs, 100);
    char **infos = backtrace_symbols(bufs, n);

    if(!infos) exit(1);

    fprintf(log, "==================\n");
    fprintf(log, "frame info:\n");

    char cmd[512];
    int len = snprintf(cmd, sizeof(cmd),
        "addr2line -ifc -e ./mytest");
    char *p = cmd + len;
    size_t s = sizeof(cmd) - len;
    for(int i = 0; i < n; ++i) {
        fprintf(log, "%s\n", infos[i]);
        if(s > 0) {
            len = snprintf(p, s, " %p", bufs[i]);
            p += len;
            s -= len;
        }
    }
    fprintf(log, "src info:\n");

    file *fp;
    char buf[128];
    if((fp = popen(cmd, "r"))) {
        while(fgets(buf, sizeof(buf), fp))
            fprintf(log, "%s", buf);
        pclose(fp);
    }
    fprintf(log, "==================\n");
    free(infos);
    // same as:
    //backtrace_symbols_fd(bufs, n, stdout_fileno);
}

void segv_handle(int s)
{
    dump_stack(stdout);

    exit(127 + s);
}


摘自 leeshuheng的专栏