捕捉信号SIGSEGV并回溯栈帧

来源:互联网 发布:php分页代码兄弟连 编辑:程序博客网 时间:2024/06/07 05:40

引出问题

在嵌入式应用程序开发过程中,调试一直是个老大难问题 -- 由于环境的限制,当程序发生段错误时不能很好的定位到底是哪里出现了错误,如果在程序发生段错误时能够清晰明了地看到程序的栈帧链,那无疑是雪中送炭。本文就捕捉信号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.   
  12.   
  13. /* 纯C环境下,不定义宏NO_CPP_DEMANGLE */  
  14. #if (!defined(__cplusplus)) && (!defined(NO_CPP_DEMANGLE))  
  15. #define NO_CPP_DEMANGLE  
  16. #endif  
  17.   
  18. #ifndef NO_CPP_DEMANGLE  
  19.     #include <cxxabi.h>  
  20.     #ifdef __cplusplus  
  21.         using __cxxabiv1::__cxa_demangle;  
  22.     #endif  
  23. #endif  
  24.   
  25. #ifdef HAS_ULSLIB  
  26.     #include <uls/logger.h>  
  27.     #define sigsegv_outp(x) sigsegv_outp(, gx)  
  28. #else  
  29.     #define sigsegv_outp(x, ...)    fprintf(stderr, x"\n", ##__VA_ARGS__)  
  30. #endif  
  31.   
  32. #if (defined __x86_64__)  
  33.     #define REGFORMAT   "%016lx"      
  34. #elif (defined __i386__)  
  35.     #define REGFORMAT   "%08x"  
  36. #elif (defined __arm__)  
  37.     #define REGFORMAT   "%lx"  
  38. #endif  
  39.   
  40. static void print_reg(ucontext_t *uc)   
  41. {  
  42. #if (defined __x86_64__) || (defined __i386__)  
  43.     int i;  
  44.     for (i = 0; i < NGREG; i++) {  
  45.         sigsegv_outp("reg[%02d]: 0x"REGFORMAT, i, uc->uc_mcontext.gregs[i]);  
  46.     }  
  47. #elif (defined __arm__)  
  48.     sigsegv_outp("reg[%02d]     = 0x"REGFORMAT, 0, uc->uc_mcontext.arm_r0);  
  49.     sigsegv_outp("reg[%02d]     = 0x"REGFORMAT, 1, uc->uc_mcontext.arm_r1);  
  50.     sigsegv_outp("reg[%02d]     = 0x"REGFORMAT, 2, uc->uc_mcontext.arm_r2);  
  51.     sigsegv_outp("reg[%02d]     = 0x"REGFORMAT, 3, uc->uc_mcontext.arm_r3);  
  52.     sigsegv_outp("reg[%02d]     = 0x"REGFORMAT, 4, uc->uc_mcontext.arm_r4);  
  53.     sigsegv_outp("reg[%02d]     = 0x"REGFORMAT, 5, uc->uc_mcontext.arm_r5);  
  54.     sigsegv_outp("reg[%02d]     = 0x"REGFORMAT, 6, uc->uc_mcontext.arm_r6);  
  55.     sigsegv_outp("reg[%02d]     = 0x"REGFORMAT, 7, uc->uc_mcontext.arm_r7);  
  56.     sigsegv_outp("reg[%02d]     = 0x"REGFORMAT, 8, uc->uc_mcontext.arm_r8);  
  57.     sigsegv_outp("reg[%02d]     = 0x"REGFORMAT, 9, uc->uc_mcontext.arm_r9);  
  58.     sigsegv_outp("reg[%02d]     = 0x"REGFORMAT, 10, uc->uc_mcontext.arm_r10);  
  59.     sigsegv_outp("FP        = 0x"REGFORMAT, uc->uc_mcontext.arm_fp);  
  60.     sigsegv_outp("IP        = 0x"REGFORMAT, uc->uc_mcontext.arm_ip);  
  61.     sigsegv_outp("SP        = 0x"REGFORMAT, uc->uc_mcontext.arm_sp);  
  62.     sigsegv_outp("LR        = 0x"REGFORMAT, uc->uc_mcontext.arm_lr);  
  63.     sigsegv_outp("PC        = 0x"REGFORMAT, uc->uc_mcontext.arm_pc);  
  64.     sigsegv_outp("CPSR      = 0x"REGFORMAT, uc->uc_mcontext.arm_cpsr);  
  65.     sigsegv_outp("Fault Address = 0x"REGFORMAT, uc->uc_mcontext.fault_address);  
  66.     sigsegv_outp("Trap no       = 0x"REGFORMAT, uc->uc_mcontext.trap_no);  
  67.     sigsegv_outp("Err Code  = 0x"REGFORMAT, uc->uc_mcontext.error_code);  
  68.     sigsegv_outp("Old Mask  = 0x"REGFORMAT, uc->uc_mcontext.oldmask);  
  69. #endif  
  70. }  
  71.   
  72. static void print_call_link(ucontext_t *uc)   
  73. {  
  74.     int i = 0;  
  75.     void **frame_pointer = (void **)NULL;  
  76.     void *return_address = NULL;  
  77.     Dl_info dl_info = { 0 };  
  78.   
  79. #if (defined __i386__)  
  80.     frame_pointer = (void **)uc->uc_mcontext.gregs[REG_EBP];  
  81.     return_address = (void *)uc->uc_mcontext.gregs[REG_EIP];  
  82. #elif (defined __x86_64__)  
  83.     frame_pointer = (void **)uc->uc_mcontext.gregs[REG_RBP];  
  84.     return_address = (void *)uc->uc_mcontext.gregs[REG_RIP];  
  85. #elif (defined __arm__)  
  86. /* sigcontext_t on ARM: 
  87.         unsigned long trap_no; 
  88.         unsigned long error_code; 
  89.         unsigned long oldmask; 
  90.         unsigned long arm_r0; 
  91.         ... 
  92.         unsigned long arm_r10; 
  93.         unsigned long arm_fp; 
  94.         unsigned long arm_ip; 
  95.         unsigned long arm_sp; 
  96.         unsigned long arm_lr; 
  97.         unsigned long arm_pc; 
  98.         unsigned long arm_cpsr; 
  99.         unsigned long fault_address; 
  100. */  
  101.     frame_pointer = (void **)uc->uc_mcontext.arm_fp;  
  102.     return_address = (void *)uc->uc_mcontext.arm_pc;  
  103. #endif  
  104.   
  105.     sigsegv_outp("\nStack trace:");  
  106.     while (frame_pointer && return_address) {  
  107.         if (!dladdr(return_address, &dl_info))  break;  
  108.         const char *sname = dl_info.dli_sname;    
  109. #ifndef NO_CPP_DEMANGLE  
  110.         int status;  
  111.         char *tmp = __cxa_demangle(sname, NULL, 0, &status);  
  112.         if (status == 0 && tmp) {  
  113.             sname = tmp;  
  114.         }  
  115. #endif  
  116.         /* No: return address <sym-name + offset> (filename) */  
  117.         sigsegv_outp("%02d: %p <%s + %lu> (%s)", ++i, return_address, sname,   
  118.             (unsigned long)return_address - (unsigned long)dl_info.dli_saddr,   
  119.                                                     dl_info.dli_fname);  
  120. #ifndef NO_CPP_DEMANGLE  
  121.         if (tmp)    free(tmp);  
  122. #endif  
  123.         if (dl_info.dli_sname && !strcmp(dl_info.dli_sname, "main")) {  
  124.             break;  
  125.         }  
  126.   
  127. #if (defined __x86_64__) || (defined __i386__)  
  128.         return_address = frame_pointer[1];  
  129.         frame_pointer = frame_pointer[0];  
  130. #elif (defined __arm__)  
  131.         return_address = frame_pointer[-1];   
  132.         frame_pointer = (void **)frame_pointer[-3];  
  133. #endif  
  134.     }  
  135.     sigsegv_outp("Stack trace end.");  
  136. }  
  137.   
  138. static void sigsegv_handler(int signo, siginfo_t *info, void *context)  
  139. {  
  140.     if (context) {  
  141.         ucontext_t *uc = (ucontext_t *)context;  
  142.   
  143.         sigsegv_outp("Segmentation Fault!");  
  144.         sigsegv_outp("info.si_signo = %d", signo);  
  145.         sigsegv_outp("info.si_errno = %d", info->si_errno);  
  146.         sigsegv_outp("info.si_code  = %d (%s)", info->si_code,   
  147.             (info->si_code == SEGV_MAPERR) ? "SEGV_MAPERR" : "SEGV_ACCERR");  
  148.         sigsegv_outp("info.si_addr  = %p\n", info->si_addr);  
  149.   
  150.         print_reg(uc);  
  151.         print_call_link(uc);  
  152.     }  
  153.   
  154.     _exit(0);  
  155. }  
  156.   
  157. #define SETSIG(sa, sig, fun, flags)     \  
  158.         do {                            \  
  159.             sa.sa_sigaction = fun;      \  
  160.             sa.sa_flags = flags;        \  
  161.             sigemptyset(&sa.sa_mask);   \  
  162.             sigaction(sig, &sa, NULL);  \  
  163.         } while(0)  
  164.   
  165. static void __attribute((constructor)) setup_sigsegv(void)   
  166. {  
  167.     struct sigaction sa;  
  168.   
  169.     SETSIG(sa, SIGSEGV, sigsegv_handler, SA_SIGINFO);   
  170. #if 0  
  171.     memset(&sa, 0, sizeof(struct sigaction));  
  172.     sa.sa_sigaction = sigsegv_handler;  
  173.     sa.sa_flags = SA_SIGINFO;  
  174.     if (sigaction(SIGSEGV, &sa, NULL) < 0) {  
  175.         perror("sigaction: ");  
  176.     }  
  177. #endif  
  178. }  
  179.   
  180. #if 1  
  181. void func3(void)  
  182. {  
  183.     char *p = (char *)0x12345678;  
  184.     *p = 10;  
  185. }  
  186.   
  187. void func2(void)  
  188. {  
  189.     func3();      
  190. }  
  191.   
  192. void func1(void)  
  193. {  
  194.     func2();  
  195. }  
  196.   
  197. int main(int argc, const char *argv[])  
  198. {  
  199.     func1();      
  200.     exit(EXIT_SUCCESS);  
  201. }  
  202. #endif  
编译时请加上-rdynamic -ldl选项!

代码地址:https://github.com/astrotycoon/sigsegv/blob/master/sigsegv.c


2016-1-17 18:29:33补充知识如下:

今天无意中发现原来gcc

相关阅读:

《 linux下利用backtrace追踪函数调用堆栈以及定位段错误》

《Stack Backtracing Inside Your Program》

《Obtaining a stack trace in C upon SIGSEGV》

参考链接:

《利用堆栈回溯、addr2line和Graphviz生成运行时函数调用图》

《Stack backtrace 的实现》

《linux backtrace()详细使用说明,分析Segmentation fault》

《Backtracing from code in ARM》(需要翻墙)

《Programmatic access to the call stack in C++》

《Stack unwinding (stack trace) with GCC》

《浅析ARM汇编语言子例程设计方法》

《自己动手实现arm函数栈帧回溯》

《Pre-mortem Backtracing》

《C++ Code Snippet - Print Stack Backtrace Programmatically with Demangled Function Names》

《内存泄露调试代码 可以解析C++ symbol》

《将信号用作 Linux 调试工具》

《Get an exception from a segfault on linux (x86 and x86_64), using some black magic !》

《broadcom6838开发环境实现函数栈追踪》

0 0
原创粉丝点击