随笔 - 137  文章 - 1  trackbacks - 0
<2018年6月>
272829303112
3456789
10111213141516
17181920212223
24252627282930
1234567

常用链接

留言簿

随笔分类

随笔档案

收藏夹

调试技巧

搜索

  •  

最新评论

阅读排行榜

评论排行榜

一般察看函数运行时堆栈的方法是使用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 长戟十三千 阅读(1568) 评论(0)  编辑 收藏 引用 所属分类: 编程技巧随笔调试

只有注册用户登录后才能发表评论。
网站导航: 博客园   IT新闻   BlogJava   知识库   博问   管理