Linux中backtrace()系列函数的应用实例
来源:互联网 发布:faker哭了知乎 编辑:程序博客网 时间:2024/06/07 00:30
一、引言
backtrace()系列函数可用来输出代码出错时的函数调用关系。
A backtrace is the series of currently active function calls for the program.
#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);
二、示例:
#include <stdio.h>#include <stdlib.h>#include <unistd.h>#include <termios.h>#include <sys/types.h>#include <sys/stat.h>#include <fcntl.h>#include <strings.h>#include <string.h>#include <errno.h>#include <signal.h>#include <sys/ioctl.h>#include <execinfo.h> #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 <dbg_backtrace>: 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 <backtrace@plt> 804867b:89 45 f0 mov %eax,-0x10(%ebp)callback: ./sig_setup() [0x8048791]08048773 <sig_handler_fatal>: 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 <printf@plt> 804878c:e8 cb fe ff ff call 804865c <dbg_backtrace> 8048791:c7 04 24 7f 00 00 00 movl $0x7f,(%esp)callback: ./sig_setup() [0x8048ae7]08048ac3 <mem_err>: 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 <printf@plt> 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
1 0
- Linux中backtrace()系列函数的应用实例
- 巧用backtrace系列函数
- linux中追踪函数backtrace调用堆栈
- Linux中exec系列函数的应用
- backtrace 函数的使用
- backtrace函数的原理
- backtrace函数的使用
- 巧用backtrace系列函数,在不具备gdb环境的Linux系统上大致定位段错误位置
- 巧用backtrace系列函数,在不具备gdb环境的Linux系统上大致定位段错误位置
- 巧用backtrace系列函数,在不具备gdb环境的Linux系统上大致定位段错误位置
- 巧用backtrace系列函数,在不具备gdb环境的Linux系统上大致定位段错误位置
- 巧用backtrace系列函数,在不具备gdb环境的Linux系统上大致定位段错误位置
- 巧用backtrace系列函数,在不具备gdb环境的Linux系统上大致定位段错误位置
- 巧用backtrace系列函数,在不具备gdb环境的Linux系统上大致定位段错误位置
- 高效获得Linux函数调用栈/backtrace的方法
- 高效获得Linux函数调用栈/backtrace的方法
- 高效获得Linux函数调用栈/backtrace的方法
- linux打印当前函数调用栈backtrace
- [LeetCode] Rotate Image 旋转图像
- 手斧Linux – 从LFS到Funtoo (23)
- ArcEngine数据删除几种方法和性能比较
- 安卓第四次实验:游戏计分以及碰撞修改
- 手斧Linux – 从LFS到Funtoo (24)
- Linux中backtrace()系列函数的应用实例
- SwingWorker
- tiny6410按键驱动(六)---异步通知
- Installing MySQL from Source on Windows
- 手斧Linux – 从LFS到Funtoo (25)
- 欢迎您在新浪博客安家
- QT中MainWindow的布局设置
- 完美解决qt(4.8.4) 中文路径问题
- windows 无窗口进程间消息通…