1. 程式人生 > >捕捉訊號SIGSEGV並回溯棧幀

捕捉訊號SIGSEGV並回溯棧幀

引出問題

嵌入式應用程式開發過程中,除錯一直是個老大難問題 -- 由於環境的限制,當程式發生段錯誤時不能很好的定位到底是哪裡出現了錯誤,如果在程式發生段錯誤時能夠清晰明瞭地看到程式的棧幀鏈,那無疑是雪中送炭。本文就捕捉訊號SIGSEGV並在該訊號的處理函式中打印出函式棧幀鏈來幫助我們除錯程式。

本文的程式適合ARM和X86平臺。

回溯棧幀原理

理解函式棧幀的佈局後,那麼自然明白回溯棧幀的原理了,這裡不多解釋了,直接上圖(來自網路):

                                                                 x86函式棧幀結構

 

                                                                ARM函式棧幀結構

程式碼sigsegv.c

[cpp] view plain copy  print?在CODE上檢視程式碼片派生到我的程式碼片
  1. #ifndef _GNU_SOURCE
  2.     #define _GNU_SOURCE
  3. #endif
  4. #include <stdio.h>
  5. #include <dlfcn.h>
  6. #include <stdlib.h>
  7. #include <signal.h>
  8. #include <unistd.h>
  9. #include <string.h>
  10. #include <ucontext.h>
  11. /* 純C環境下,不定義巨集NO_CPP_DEMANGLE */
  12. #if (!defined(__cplusplus)) && (!defined(NO_CPP_DEMANGLE))
  13. #define NO_CPP_DEMANGLE
  14. #endif
  15. #ifndef NO_CPP_DEMANGLE
  16.     #include <cxxabi.h>
  17.     #ifdef __cplusplus
  18.         using __cxxabiv1::__cxa_demangle;  
  19.     #endif
  20. #endif
  21. #ifdef HAS_ULSLIB
  22.     #include <uls/logger.h>
  23.     #define sigsegv_outp(x) sigsegv_outp(, gx)
  24. #else
  25.     #define sigsegv_outp(x, ...)    fprintf(stderr, x"\n", ##__VA_ARGS__)
  26. #endif
  27. #if (defined __x86_64__)
  28.     #define REGFORMAT   "%016lx"    
  29. #elif (defined __i386__)
  30.     #define REGFORMAT   "%08x"
  31. #elif (defined __arm__)
  32.     #define REGFORMAT   "%lx"
  33. #endif
  34. staticvoid print_reg(ucontext_t *uc)   
  35. {  
  36. #if (defined __x86_64__) || (defined __i386__)
  37.     int i;  
  38.     for (i = 0; i < NGREG; i++) {  
  39.         sigsegv_outp("reg[%02d]: 0x"REGFORMAT, i, uc->uc_mcontext.gregs[i]);  
  40.     }  
  41. #elif (defined __arm__)
  42.     sigsegv_outp("reg[%02d]     = 0x"REGFORMAT, 0, uc->uc_mcontext.arm_r0);  
  43.     sigsegv_outp("reg[%02d]     = 0x"REGFORMAT, 1, uc->uc_mcontext.arm_r1);  
  44.     sigsegv_outp("reg[%02d]     = 0x"REGFORMAT, 2, uc->uc_mcontext.arm_r2);  
  45.     sigsegv_outp("reg[%02d]     = 0x"REGFORMAT, 3, uc->uc_mcontext.arm_r3);  
  46.     sigsegv_outp("reg[%02d]     = 0x"REGFORMAT, 4, uc->uc_mcontext.arm_r4);  
  47.     sigsegv_outp("reg[%02d]     = 0x"REGFORMAT, 5, uc->uc_mcontext.arm_r5);  
  48.     sigsegv_outp("reg[%02d]     = 0x"REGFORMAT, 6, uc->uc_mcontext.arm_r6);  
  49.     sigsegv_outp("reg[%02d]     = 0x"REGFORMAT, 7, uc->uc_mcontext.arm_r7);  
  50.     sigsegv_outp("reg[%02d]     = 0x"REGFORMAT, 8, uc->uc_mcontext.arm_r8);  
  51.     sigsegv_outp("reg[%02d]     = 0x"REGFORMAT, 9, uc->uc_mcontext.arm_r9);  
  52.     sigsegv_outp("reg[%02d]     = 0x"REGFORMAT, 10, uc->uc_mcontext.arm_r10);  
  53.     sigsegv_outp("FP        = 0x"REGFORMAT, uc->uc_mcontext.arm_fp);  
  54.     sigsegv_outp("IP        = 0x"REGFORMAT, uc->uc_mcontext.arm_ip);  
  55.     sigsegv_outp("SP        = 0x"REGFORMAT, uc->uc_mcontext.arm_sp);  
  56.     sigsegv_outp("LR        = 0x"REGFORMAT, uc->uc_mcontext.arm_lr);  
  57.     sigsegv_outp("PC        = 0x"REGFORMAT, uc->uc_mcontext.arm_pc);  
  58.     sigsegv_outp("CPSR      = 0x"REGFORMAT, uc->uc_mcontext.arm_cpsr);  
  59.     sigsegv_outp("Fault Address = 0x"REGFORMAT, uc->uc_mcontext.fault_address);  
  60.     sigsegv_outp("Trap no       = 0x"REGFORMAT, uc->uc_mcontext.trap_no);  
  61.     sigsegv_outp("Err Code  = 0x"REGFORMAT, uc->uc_mcontext.error_code);  
  62.     sigsegv_outp("Old Mask  = 0x"REGFORMAT, uc->uc_mcontext.oldmask);  
  63. #endif
  64. }  
  65. staticvoid print_call_link(ucontext_t *uc)   
  66. {  
  67.     int i = 0;  
  68.     void **frame_pointer = (void **)NULL;  
  69.     void *return_address = NULL;  
  70.     Dl_info dl_info = { 0 };  
  71. #if (defined __i386__)
  72.     frame_pointer = (void **)uc->uc_mcontext.gregs[REG_EBP];  
  73.     return_address = (void *)uc->uc_mcontext.gregs[REG_EIP];  
  74. #elif (defined __x86_64__)
  75.     frame_pointer = (void **)uc->uc_mcontext.gregs[REG_RBP];  
  76.     return_address = (void *)uc->uc_mcontext.gregs[REG_RIP];  
  77. #elif (defined __arm__)
  78. /* sigcontext_t on ARM: 
  79.         unsigned long trap_no; 
  80.         unsigned long error_code; 
  81.         unsigned long oldmask; 
  82.         unsigned long arm_r0; 
  83.         ... 
  84.         unsigned long arm_r10; 
  85.         unsigned long arm_fp; 
  86.         unsigned long arm_ip; 
  87.         unsigned long arm_sp; 
  88.         unsigned long arm_lr; 
  89.         unsigned long arm_pc; 
  90.         unsigned long arm_cpsr; 
  91.         unsigned long fault_address; 
  92. */
  93.     frame_pointer = (void **)uc->uc_mcontext.arm_fp;  
  94.     return_address = (void *)uc->uc_mcontext.arm_pc;  
  95. #endif
  96.     sigsegv_outp("\nStack trace:");  
  97.     while (frame_pointer && return_address) {  
  98.         if (!dladdr(return_address, &dl_info))  break;  
  99.         constchar *sname = dl_info.dli_sname;    
  100. #ifndef NO_CPP_DEMANGLE
  101.         int status;  
  102.         char *tmp = __cxa_demangle(sname, NULL, 0, &status);  
  103.         if (status == 0 && tmp) {  
  104.             sname = tmp;  
  105.         }  
  106. #endif
  107.         /* No: return address <sym-name + offset> (filename) */
  108.         sigsegv_outp("%02d: %p <%s + %lu> (%s)", ++i, return_address, sname,   
  109.             (unsigned long)return_address - (unsigned long)dl_info.dli_saddr,   
  110.                                                     dl_info.dli_fname);  
  111. #ifndef NO_CPP_DEMANGLE
  112.         if (tmp)    free(tmp);  
  113. #endif
  114.         if (dl_info.dli_sname && !strcmp(dl_info.dli_sname, "main")) {  
  115.             break;  
  116.         }  
  117. #if (defined __x86_64__) || (defined __i386__)
  118.         return_address = frame_pointer[1];  
  119.         frame_pointer = frame_pointer[0];  
  120. #elif (defined __arm__)
  121.         return_address = frame_pointer[-1];   
  122.         frame_pointer = (void **)frame_pointer[-3];  
  123. #endif
  124.     }  
  125.     sigsegv_outp("Stack trace end.");  
  126. }  
  127. staticvoid sigsegv_handler(int signo, siginfo_t *info, void *context)  
  128. {  
  129.     if (context) {  
  130.         ucontext_t *uc = (ucontext_t *)context;  
  131.         sigsegv_outp("Segmentation Fault!");  
  132.         sigsegv_outp("info.si_signo = %d", signo);  
  133.         sigsegv_outp("info.si_errno = %d", info->si_errno);  
  134.         sigsegv_outp("info.si_code  = %d (%s)", info->si_code,   
  135.             (info->si_code == SEGV_MAPERR) ? "SEGV_MAPERR" : "SEGV_ACCERR");  
  136.         sigsegv_outp("info.si_addr  = %p\n", info->si_addr);  
  137.         print_reg(uc);  
  138.         print_call_link(uc);  
  139.     }  
  140.     _exit(0);  
  141. }  
  142. #define SETSIG(sa, sig, fun, flags)     \
  143.         do {                            \  
  144.             sa.sa_sigaction = fun;      \  
  145.             sa.sa_flags = flags;        \  
  146.             sigemptyset(&sa.sa_mask);   \  
  147.             sigaction(sig, &sa, NULL);  \  
  148.         } while(0)  
  149. staticvoid __attribute((constructor)) setup_sigsegv(void)   
  150. {  
  151.     struct sigaction sa;  
  152.     SETSIG(sa, SIGSEGV, sigsegv_handler, SA_SIGINFO);   
  153. #if 0
  154.     memset(&sa, 0, sizeof(struct sigaction));  
  155.     sa.sa_sigaction = sigsegv_handler;  
  156.     sa.sa_flags = SA_SIGINFO;  
  157.     if (sigaction(SIGSEGV, &sa, NULL) < 0) {  
  158.         perror("sigaction: ");  
  159.     }  
  160. #endif
  161. }  
  162. #if 1
  163. void func3(void)  
  164. {  
  165.     char *p = (char *)0x12345678;  
  166.     *p = 10;  
  167. }  
  168. void func2(void)  
  169. {  
  170.     func3();      
  171. }  
  172. void func1(void)  
  173. {  
  174.     func2();  
  175. }  
  176. int main(int argc, constchar *argv[])  
  177. {  
  178.     func1();      
  179.     exit(EXIT_SUCCESS);  
  180. }  
  181. #endif
編譯時請加上-rdynamic -ldl選項!

2016-1-17 18:29:33補充知識如下:

今天無意中發現原來gcc

相關閱讀:

參考連結: