linux 内核backtrace,Linux中backtrace()系列函数的应用实例

一、引言

backtrace()系列函数可用来输出代码出错时的函数调用关系。

A backtrace is the series of currently active function calls for the program.

#include

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);

二、示例:

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#define SIZE(100)

static sigset_t signals_handled;

static void dbg_backtrace(void)

{

int i, nline;

void *buffer[SIZE];

char **strings;

nline = backtrace(buffer, SIZE);

printf("addr: %d = backtrace()\n", nline);

strings = backtrace_symbols(buffer, nline);

if (NULL == strings) {

perror("backtrace_symbols()\n");

exit(EXIT_FAILURE);

}

for (i = 0; i < nline; i ++) {

printf("callback: %s\n", strings[j]);

}

/* backtrace_symbols() 内部有调用malloc函数 */

free(strings);

}

static void sig_handler_hup(int sig)

{

printf("%s() %d\n\n", __func__, sig);

}

static void sig_handler_debug(int sig)

{

printf("%s() %d\n\n", __func__, sig);

}

static void sig_handler_usr2(int sig)

{

printf("%s() %d\n\n", __func__, sig);

}

static void sig_handler_fatal(int sig)

{

printf("Fatal signal %d\n\n", sig);

dbg_backtrace();

exit(127);

}

void signals_setup(void)

{

struct sigaction sa;

sigemptyset(&signals_handled);

sigaddset(&signals_handled, SIGHUP);

sigaddset(&signals_handled, SIGINT);

sigaddset(&signals_handled, SIGTERM);

sigaddset(&signals_handled, SIGUSR2);

#define SIGNAL(s, handler)do { \

sa.sa_handler = handler;\

if (sigaction(s, &sa, NULL) < 0) \

printf("could not set signal handler (%d): %m", s); \

} while(0);

sa.sa_mask = signals_handled;

sa.sa_flags = 0;

SIGNAL(SIGHUP, sig_handler_hup);

SIGNAL(SIGUSR1, sig_handler_debug);

SIGNAL(SIGUSR2, sig_handler_usr2);

SIGNAL(SIGABRT, sig_handler_fatal);

SIGNAL(SIGALRM, sig_handler_fatal);

SIGNAL(SIGFPE, sig_handler_fatal);

SIGNAL(SIGILL, sig_handler_fatal);

SIGNAL(SIGPIPE, sig_handler_fatal);

SIGNAL(SIGQUIT, sig_handler_fatal);

SIGNAL(SIGSEGV, sig_handler_fatal);

signal(SIGPIPE, SIG_IGN);

}

void mem_err(void)

{

char *prt = NULL;

printf("%s()\n", __func__);

*prt = '1';

}

int main(int argc, char **argv)

{

signals_setup();

int i = 0;

for (; ;) {

i ++;

if (i > 10) {

mem_err();

}

// printf("i: %d\n", i);

sleep(2);

}

return 0;

}

运行结果:

mem_err()

Fatal signal 11

addr: 7 = backtrace()

callback: ./sig_setup() [0x804867b]

callback: ./sig_setup() [0x8048791]

callback: [0xb77af400]

callback: ./sig_setup() [0x8048ae7]

callback: ./sig_setup() [0x8048b13]

callback: /lib/i386-linux-gnu/i686/cmov/libc.so.6(__libc_start_main+0xe6) [0xb764ce66]

callback: ./sig_setup() [0x8048591]

三、backtrace()系列函数含义:

int backtrace(void **buffer, int size);

backtrace()返回程序调用的backtrace信息,结果(即地址:address from the corresponding stack frame)存放在buffer指针指向的数组中。

size参数指定了buffer指向的数组可保存的结果的最大个数;若结果个数大于size,数组将保存那些最近执行的函数调用。

返回值代表buffer指向的数组实际元素的个数。

char **backtrace_symbols(void *const *buffer, int size);

backtrace()返回的数组中存放的结果是地址值,backtrace_symbols()则把这些地址转化成对应的字符串。

每一个元素(字串)包含地址值对应的函数名(若不能获取,则不存在)、内部语句相对函数的偏移地址(hexadecimal)、语句的实际地址(hexadecimal)。

如:./prog(myfunc3+0x5c) [0x80487f0]

backtrace_symbols()函数内部进行了malloc操作,所以调用者必须free内存。

操作执行失败返回NULL。

void backtrace_symbols_fd(void *const *buffer, int size, int fd);

功能和backtrace_symbols()函数一样,只是它把返回结果按行写到了fd代表的文件里。

backtrace_symbols_fd函数内部没有进行malloc操作!

四、结果分析:

先执行反汇编:objdump -d test > dump.txt

callback: ./sig_setup() [0x804867b]:

0804865c :

804865c:55 push %ebp

804865d:89 e5 mov %esp,%ebp

804865f:81 ec b8 01 00 00 sub $0x1b8,%esp

8048665:c7 44 24 04 64 00 00 movl $0x64,0x4(%esp)

804866c:00

804866d:8d 85 5c fe ff ff lea -0x1a4(%ebp),%eax

8048673:89 04 24 mov %eax,(%esp)

8048676:e8 e5 fe ff ff call 8048560

804867b:89 45 f0 mov %eax,-0x10(%ebp)

callback: ./sig_setup() [0x8048791]

08048773 :

8048773:55 push %ebp

8048774:89 e5 mov %esp,%ebp

8048776:83 ec 18 sub $0x18,%esp

8048779:8b 45 08 mov 0x8(%ebp),%eax

804877c:89 44 24 04 mov %eax,0x4(%esp)

8048780:c7 04 24 15 8c 04 08 movl $0x8048c15,(%esp)

8048787:e8 14 fd ff ff call 80484a0

804878c:e8 cb fe ff ff call 804865c

8048791:c7 04 24 7f 00 00 00 movl $0x7f,(%esp)

callback: ./sig_setup() [0x8048ae7]

08048ac3 :

8048ac3:55 push %ebp

8048ac4:89 e5 mov %esp,%ebp

8048ac6:83 ec 28 sub $0x28,%esp

8048ac9:c7 45 f4 00 00 00 00 movl $0x0,-0xc(%ebp)

8048ad0:c7 44 24 04 5b 8c 04 movl $0x8048c5b,0x4(%esp)

8048ad7:08

8048ad8:c7 04 24 4e 8c 04 08 movl $0x8048c4e,(%esp)

8048adf:e8 bc f9 ff ff call 80484a0

8048ae4:8b 45 f4 mov -0xc(%ebp),%eax

8048ae7:c6 00 31 movb $0x31,(%eax)

至此可知,这里的函数调用,造成了非法的内存操作。

五、backtrace()系列函数注意事项:

这三个函数都假设函数的返回地址按它认为的方式保存在栈上,故使用时应注意:

1、帧指针(Frame  pointers)的屏蔽/忽略可能导致上述假设失效

2、inline关键字描述的函数没有栈帧(stack frames)

3、尾调用(Tail-call)优化会造成一个栈帧被另一个替换掉

4、对于特定编译器,应该指定链接选项,否则函数名字段可能是无效的;对于使用GNU linker的系统,使用-rdynamic链接项

5、static关键字描述的函数不会被显示,and won't be available in the backtrace

PS:

栈帧(stack frames):

In computer science, a stack frame is a memory management strategy used to create and destroy temporary (automatic) variables in some programming languages. Stack frames only exist at run-time.

在计算机科学里,栈帧是一种内存管理策略,在某些编程语言里它用来创建/销毁临时(自动)变量。栈帧只在运行时存在。

尾调用(Tail-call):

In computer science, a tail call is a subroutine call performed as the final action of a procedure.

在计算机科学里,尾调用是指一个函数里的最后一个动作是一个函数调用的情形:即这个调用的返回值直接被当前函数返回的情形。

参考资料:

帧指针(Frame  pointers):http://stackoverflow.com/questions/579262/what-is-the-purpose-of-the-frame-pointer

http://blog.chinaunix.net/uid-25871104-id-2938389.html

栈帧(stack frames):http://en.citizendium.org/wiki/Stack_frame

尾调用(Tail-call):http://en.wikipedia.org/wiki/Tail_call

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值