【转】调试段错误方法


0 引言

     在调试应用程序时,可能因各种原因导致运行发生段错误。因此,有必要将堆栈调用信息打印出来,方便定位错误。

1 方法

     在glibc头文件execinfo.h中提供了三个函数获取当前线程的函数调用堆栈。

0)  int backtrace(void **buffer,  intsize)

该函数将获取的堆栈信息保存至buffer中,参数size则是表示buffer大小

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

backtrace_symbols将从backtrace函数获取的信息转换一个字符串数组,参数buffer是从backtrace函数获取的指针数组。函数返回值是一个指向字符串数组的指针,它的大小同buffer相同,每个字符串包含了一个相对于buffer中对应元素的可打印的信息,包括函数名,函数偏移地址和实际返回地址。很可惜的是只有使用ELF二进制格式的程序才能获取函数名和函数偏移地址。其它格式文件只能返回实际返回地址。

注意:某些编译器的优化选项对获取正确的调用堆栈有干扰(如果源代码编译时使用了-O1或-O2优化选项,可执行代码会把ebp/rbp/rsp寄存器当作普通寄存器使用,导致backtrace失败。为了防止这种情况发生,可以在编译时使用-O2 –fno-omit-frame-pointer或-Og来避免优化中使用上述寄存器。),另外内联函数没有堆栈框架。另外,bactrace_symbols与backtrace不同,返回值需要手动free,即malloc是在函数内部实现中,但free交由调用者执行。

       程序举例如下;

#include <stdio.h>

#include <stdlib.h>

#include <stddef.h>

#include <execinfo.h>

#include <signal.h>

 

void dump(int signo)

{

  void *buffer[30] = { 0 };

  size_t size;

  char **strings = NULL;

  size_t i = 0;

 

  size = backtrace(buffer, 30);

  fprintf(stdout, "Obtained %zd stack frames.nm\n", size);

  strings = backtrace_symbols(buffer, size);

  if (strings == NULL)

  {

    perror("backtrace_symbols.");

    exit(EXIT_FAILURE);

  }

 

  for (i = 0; i < size; i++)

  {

    fprintf(stdout, "%s\n", strings[i]);

  }

  free(strings);

  strings = NULL;

  exit(0);

}

 

void func_c()

{

  *((volatile char *)0x0) = 0x9999;

}

 

void func_b()

{

  func_c();

}

 

void func_a()

{

  func_b();

}

 

int main(int argc, const char *argv[])

{

  if (signal(SIGSEGV, dump) == SIG_ERR)

    perror("can't catch SIGSEGV");

  func_a();

  return 0;

}

2 addr2line工具

     正如上所述,通常情况我们并不能拿到函数名,而是返回的地址。庆幸的是,gcc工具链中提供了一个将返回地址映射到函数文件的具体位置的工具----addr2line工具。

       具体使用方法也比较简单,-e 可执行程序文件名,用于读取符号信息(因此要求输入的文件是debug文件)。

3 其它方法

       即基于BSP寄存器,手动解析堆栈调用关系。可以参考 http://blog.csdn.net/littlefang/article/details/42295803和http://blog.chinaunix.net/uid-24774106-id-3457205.html文章。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章