记录两种程序崩溃时查找方法
堆栈回溯实践
总结
目前堆栈回溯存在两套代码:
- 代码1使用系统库函数
backtrace()自动进行堆栈回溯,在x86,mips,arm架构上实践证明有效. - 代码2手动进行堆栈回溯,通过
fp,ra等指针,一层一层找到调用的函数地址,并通过函数dladdr()得到函数名,使用此函数时链接选项添加-ldl,头文件为dlfcn.h。这种方案在x86,arm架构上有效,mips的堆栈帧太过复杂,目前没有手动回溯成功。
通过工具链自定义的宏控,可以将两套代码合并,使用在我们的平台下。
编译时用到的小知识
- 使用
gcc -E -dM - </dev/null查看工具链预定义的宏,如x86工具链预定义了__x86_64__,类似可以知道arm,mips预定义的类似宏(在方案二的代码中用到)。 - 使用
gcc -E -v - </dev/null查看交叉工具链的头文件与库文件搜索路径,方便我们使用交叉工具链进行编译。
编译注意事项
- 使用
-g -dynamic产生必要的调试信息 < -g -dynamic -ldl> 个人觉得直接这样加上比较省事 - 若手动进行符号解析,需要使用
dladdr函数,链接选项添加-ldl - arm下编译选项需要带有
-mapcs-frame:对所有函数都生成一个遵从ARM程序调用标准的堆栈帧,即使在正确执行代码无需严格这么做时。使用此开关时指定“-fomit-frame-pointer”将不产生叶函数的堆栈帧。缺省情况下是“-mno-apcs-frame”。使用此编译选项,方案2即可有效。参考https://blog.csdn.net/weixin_30270561/article/details/94854486 - arm下编译选项需要带有
-mapcs-frame -fexceptions方案1才有效。 - mips下可能需要编译选项
-fexceptions -fno-omit-frame-pointer -O0(没有严格验证,只是为防止意外)代码1(mips,arm,x86下有效)
#include <stdio.h> #include <execinfo.h> #include <signal.h> #include <stdlib.h> #include <unistd.h> void handler(int sig) { void *array[10]; size_t size; // get void*'s for all entries on the stack size = backtrace(array, 10); // print out all the frames to stderr fprintf(stderr, "Error: signal %d:\n", sig); backtrace_symbols_fd(array, size, STDERR_FILENO); exit(1); } void baz() { //int *foo = (int*)-1; // make a bad pointer int *foo =(int*)123; printf("%d\n", *foo); // causes segfault } void bar() { baz(); } void foo() { bar(); } int main(int argc, char **argv) { signal(SIGSEGV, handler); // install our handler foo(); // this will call foo, bar, and baz. baz segfaults. }
代码2(x86,arm下有效,mips下无效)
#ifndef _GNU_SOURCE #define _GNU_SOURCE #endif #include <stdio.h> #include <dlfcn.h> #include <stdlib.h> #include <signal.h> #include <unistd.h> #include <string.h> #include <ucontext.h> /* 纯C环境下,不定义宏NO_CPP_DEMANGLE */ #if (!defined(__cplusplus)) && (!defined(NO_CPP_DEMANGLE)) #define NO_CPP_DEMANGLE #endif #ifndef NO_CPP_DEMANGLE #include <cxxabi.h> #ifdef __cplusplus using __cxxabiv1::__cxa_demangle; #endif #endif #ifdef HAS_ULSLIB #include <uls/logger.h> #define sigsegv_outp(x) sigsegv_outp(, gx) #else #define sigsegv_outp(x, ...) fprintf(stderr, x"\n", ##__VA_ARGS__) #endif #if (defined __x86_64__) #define REGFORMAT "%016lx" #elif (defined __i386__) #define REGFORMAT "%08x" #elif (defined __arm__) #define REGFORMAT "%lx" #endif static void print_reg(ucontext_t *uc) { #if (defined __x86_64__) || (defined __i386__) int i; for (i = 0; i < NGREG; i++) { sigsegv_outp("reg[%02d]: 0x"REGFORMAT, i, uc->uc_mcontext.gregs[i]); } #elif (defined __arm__) sigsegv_outp("reg[%02d] = 0x"REGFORMAT, 0, uc->uc_mcontext.arm_r0); sigsegv_outp("reg[%02d] = 0x"REGFORMAT, 1, uc->uc_mcontext.arm_r1); sigsegv_outp("reg[%02d] = 0x"REGFORMAT, 2, uc->uc_mcontext.arm_r2); sigsegv_outp("reg[%02d] = 0x"REGFORMAT, 3, uc->uc_mcontext.arm_r3); sigsegv_outp("reg[%02d] = 0x"REGFORMAT, 4, uc->uc_mcontext.arm_r4); sigsegv_outp("reg[%02d] = 0x"REGFORMAT, 5, uc->uc_mcontext.arm_r5); sigsegv_outp("reg[%02d] = 0x"REGFORMAT, 6, uc->uc_mcontext.arm_r6); sigsegv_outp("reg[%02d] = 0x"REGFORMAT, 7, uc->uc_mcontext.arm_r7); sigsegv_outp("reg[%02d] = 0x"REGFORMAT, 8, uc->uc_mcontext.arm_r8); sigsegv_outp("reg[%02d] = 0x"REGFORMAT, 9, uc->uc_mcontext.arm_r9); sigsegv_outp("reg[%02d] = 0x"REGFORMAT, 10, uc->uc_mcontext.arm_r10); sigsegv_outp("FP = 0x"REGFORMAT, uc->uc_mcontext.arm_fp); sigsegv_outp("IP = 0x"REGFORMAT, uc->uc_mcontext.arm_ip); sigsegv_outp("SP = 0x"REGFORMAT, uc->uc_mcontext.arm_sp); sigsegv_outp("LR = 0x"REGFORMAT, uc->uc_mcontext.arm_lr); sigsegv_outp("PC = 0x"REGFORMAT, uc->uc_mcontext.arm_pc); sigsegv_outp("CPSR = 0x"REGFORMAT, uc->uc_mcontext.arm_cpsr); sigsegv_outp("Fault Address = 0x"REGFORMAT, uc->uc_mcontext.fault_address); sigsegv_outp("Trap no = 0x"REGFORMAT, uc->uc_mcontext.trap_no); sigsegv_outp("Err Code = 0x"REGFORMAT, uc->uc_mcontext.error_code); sigsegv_outp("Old Mask = 0x"REGFORMAT, uc->uc_mcontext.oldmask); #endif } static void print_call_link(ucontext_t *uc) { int i = 0; void **frame_pointer = (void **)NULL; void *return_address = NULL; Dl_info dl_info = { 0 }; #if (defined __i386__) frame_pointer = (void **)uc->uc_mcontext.gregs[REG_EBP]; return_address = (void *)uc->uc_mcontext.gregs[REG_EIP]; #elif (defined __x86_64__) frame_pointer = (void **)uc->uc_mcontext.gregs[REG_RBP]; return_address = (void *)uc->uc_mcontext.gregs[REG_RIP]; #elif (defined __arm__) /* sigcontext_t on ARM: unsigned long trap_no; unsigned long error_code; unsigned long oldmask; unsigned long arm_r0; ... unsigned long arm_r10; unsigned long arm_fp; unsigned long arm_ip; unsigned long arm_sp; unsigned long arm_lr; unsigned long arm_pc; unsigned long arm_cpsr; unsigned long fault_address; */ frame_pointer = (void **)uc->uc_mcontext.arm_fp; return_address = (void *)uc->uc_mcontext.arm_pc; #endif sigsegv_outp("\nStack trace:"); while (frame_pointer && return_address) { if (!dladdr(return_address, &dl_info)) break; const char *sname = dl_info.dli_sname; #ifndef NO_CPP_DEMANGLE int status; char *tmp = __cxa_demangle(sname, NULL, 0, &status); if (status == 0 && tmp) { sname = tmp; } #endif /* No: return address <sym-name + offset> (filename) */ sigsegv_outp("%02d: %p <%s + %lu> (%s)", ++i, return_address, sname, (unsigned long)return_address - (unsigned long)dl_info.dli_saddr, dl_info.dli_fname); #ifndef NO_CPP_DEMANGLE if (tmp) free(tmp); #endif if (dl_info.dli_sname && !strcmp(dl_info.dli_sname, "main")) { break; } #if (defined __x86_64__) || (defined __i386__) return_address = frame_pointer[1]; frame_pointer = frame_pointer[0]; #elif (defined __arm__) return_address = frame_pointer[-1]; frame_pointer = (void **)frame_pointer[-3]; #endif } sigsegv_outp("Stack trace end."); } static void sigsegv_handler(int signo, siginfo_t *info, void *context) { if (context) { ucontext_t *uc = (ucontext_t *)context; sigsegv_outp("Segmentation Fault!"); sigsegv_outp("info.si_signo = %d", signo); sigsegv_outp("info.si_errno = %d", info->si_errno); sigsegv_outp("info.si_code = %d (%s)", info->si_code, (info->si_code == SEGV_MAPERR) ? "SEGV_MAPERR" : "SEGV_ACCERR"); sigsegv_outp("info.si_addr = %p\n", info->si_addr); print_reg(uc); print_call_link(uc); } _exit(0); } #define SETSIG(sa, sig, fun, flags) \ do { \ sa.sa_sigaction = fun; \ sa.sa_flags = flags; \ sigemptyset(&sa.sa_mask); \ sigaction(sig, &sa, NULL); \ } while(0) static void __attribute((constructor)) setup_sigsegv(void) { struct sigaction sa; SETSIG(sa, SIGSEGV, sigsegv_handler, SA_SIGINFO); #if 0 memset(&sa, 0, sizeof(struct sigaction)); sa.sa_sigaction = sigsegv_handler; sa.sa_flags = SA_SIGINFO; if (sigaction(SIGSEGV, &sa, NULL) < 0) { perror("sigaction: "); } #endif } #if 1 void func3(void) { char *p = (char *)0x12345678; *p = 10; } void func2(void) { func3(); } void func1(void) { func2(); } int main(int argc, const char *argv[]) { func1(); exit(EXIT_SUCCESS); } #endif
参考链接:https://www.cnblogs.com/followthemaster/p/14171282.html

浙公网安备 33010602011771号