定位线程Segment fault (SIGSEGV)的方法

来源:互联网 发布:域名注册软件 编辑:程序博客网 时间:2024/05/28 15:07

引出问题

在嵌入式应用程序开发过程中,调试一直是个老大难问题 -- 由于环境的限制,当程序发生段错误时不能很好的定位到底是哪里出现了错误,如果在程序发生段错误时能够清晰明了地看到程序的栈帧链,那无疑是雪中送炭。本文就捕捉信号SIGSEGV并在该信号的处理函数中打印出函数栈帧链来帮助我们调试程序。

本文的程序适合ARM和X86平台。

回溯栈帧原理

理解函数栈帧的布局后,那么自然明白回溯栈帧的原理了,这里不多解释了,直接上图(来自网络):

                                                                 x86函数栈帧结构

 

                                                                ARM函数栈帧结构

代码sigsegv.c

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

编译时请加上-rdynamic -ldl选项!

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

参考链接:

http://blog.csdn.net/arnoldlu/article/details/12776455

http://www.alivepea.me/prog/how-backtrace-work/

http://velep.com/archives/1032.html

0 0