【轉】調試段錯誤方法


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文章。

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