一般察看函数运行时堆栈的方法是使用GDB之类的外部调试器,但是,有些时候为了分析程序的BUG,(主要针对长时间运行程序的分析),在程序出错时打印出函数的调用堆栈是非常有用的。
在头文件"execinfo.h"中声明了三个函数用于获取当前线程的函数调用堆栈
Function: int backtrace(void **buffer,int size)
该函数用与获取当前线程的调用堆栈,获取的信息将会被存放在buffer中,它是一个指针列表。参数 size 用来指定buffer中可以保存多少个void* 元素。函数返回值是实际获取的指针个数,最大不超过size大小,在buffer中的指针实际是从堆栈中获取的返回地址,每一个堆栈框架有一个返回地址,注意某些编译器的优化选项对获取正确的调用堆栈有干扰,另外内联函数没有堆栈框架;删除框架指针也会使无法正确解析堆栈内容。
Function: char ** backtrace_symbols (void *const *buffer, int size)
backtrace_symbols将从backtrace函数获取的信息转化为一个字符串数组. 参数buffer应该是从backtrace函数获取的数组指针,size是该数组中的元素个数(backtrace的返回值)。函数返回值是一个指向字符串数组的指针,它的大小同buffer相同.每个字符串包含了一个相对于buffer中对应元素的可打印信息.它包括函数名,函数的偏移地址,和实际的返回地址。
现在,只有使用ELF二进制格式的程序和苦衷才能获取函数名称和偏移地址.在其他系统,只有16进制的返回地址能被获取.另外,你可能需要传递相应的标志给链接器,以能支持函数名功能(比如,在使用GNU ld的系统中,你需要传递(-rdynamic))。
该函数的返回值是通过malloc函数申请的空间,因此调用这必须使用free函数来释放指针.
注意:如果不能为字符串获取足够的空间函数的返回值将会为NULL
Function:void backtrace_symbols_fd (void *const *buffer, int size, int fd)
backtrace_symbols_fd与backtrace_symbols 函数具有相同的功能,不同的是它不会给调用者返回字符串数组,而是将结果写入文件描述符为fd的文件中,每个函数对应一行.它不需要调用malloc函数,因此适用于有可能调用该函数会失败的情况。
下面是一个使用backtrace捕获异常并打印函数调用堆栈的例子:
#include <signal.h> #include <stdio.h> #include <stdlib.h> #include <execinfo.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <string.h> #include <unistd.h> #define PRINT_DEBUG static void print_reason(int sig) { void *array[10]; size_t size; size = backtrace(array, 10); #ifdef PRINT_DEBUG char **strings; int i; strings = backtrace_symbols(array, size); printf("Obtained %d stack frames.\n", size); for (i = 0; i < size; i++) printf("%s\n", strings[i]); free(strings); char cmd[64] = "addr2line -C -f -e "; char* prog = cmd + strlen(cmd); readlink("/proc/self/exe", prog, sizeof(cmd) - strlen(cmd) - 1);// 获取进程的完整路径 FILE* fp = popen(cmd, "w"); if (fp != NULL) { for (i = 0; i < size; ++i) { fprintf(fp, "%p\n", array[i]); } pclose(fp); } #else int fd = open("err.log", O_CREAT | O_WRONLY); 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_handler = print_reason; sigemptyset(&myAction.sa_mask); myAction.sa_flags = SA_RESTART | SA_SIGINFO; sigaction(SIGSEGV, &myAction, NULL); // 无效内存引用 sigaction(SIGABRT, &myAction, NULL); // 异常终止 test1(); }
我本机测试打印出的信息如下:
Obtained 7 stack frames. /root/workspace/test/Debug/test(__gxx_personality_v0+0x12d) [0x80486c1] [0x71b440] /root/workspace/test/Debug/test(__gxx_personality_v0+0x2ac) [0x8048840] /root/workspace/test/Debug/test(__gxx_personality_v0+0x2c0) [0x8048854] /root/workspace/test/Debug/test(__gxx_personality_v0+0x339) [0x80488cd] /lib/libc.so.6(__libc_start_main+0xdc) [0xbf3e9c] /root/workspace/test/Debug/test(__gxx_personality_v0+0x5d) [0x80485f1] print_reason /root/workspace/test/Debug/../main.cpp:15 ?? ??:0 die() /root/workspace/test/Debug/../main.cpp:51 test1() /root/workspace/test/Debug/../main.cpp:56 main /root/workspace/test/Debug/../main.cpp:65 ?? ??:0 _start ??:0
posted on 2018-06-29 17:03
长戟十三千 阅读(1591)
评论(0) 编辑 收藏 引用 所属分类:
编程技巧随笔 、
调试