您的位置:首页 > 其它

崩溃时打印堆栈调用日志

2012-02-08 08:45 218 查看
在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);

}

=========================================================

参考文献:backtrace_symbols(3) addr2line(1)
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: