跳转至

c语言中实现栈回溯

libunwind库实现栈回溯

CMakeLists.txt使用libunwind库

pkg_check_modules(UNWIND libunwind-generic)
target_include_directories(gdmain-iot PUBLIC ${UNWIND_INCLUDE_DIRS})
target_link_libraries(gdmain-iot PUBLIC ${UNWIND_LIBRARIES})

异常时栈回溯:

#include <signal.h>
#include <libunwind.h>
#include <cxxabi.h>

void sigsegv_handler(int sig, siginfo_t *info, void *secret)
{
  // show backtrace
  unw_cursor_t cursor; unw_context_t uc;
  unw_word_t ip, sp;
  unw_getcontext(&uc);
  unw_init_local(&cursor, &uc);
  while (unw_step(&cursor) > 0) {
    unw_word_t  offset, pc;
    char fname[64];
    fname[0] = '\0';
    unw_get_reg(&cursor, UNW_REG_IP, &pc);
    (void) unw_get_proc_name(&cursor, fname, sizeof(fname), &offset);
    C_LOG("%p: (%s+0x%x) [%s]", pc, fname, offset, abi::__cxa_demangle(fname, NULL, NULL, NULL));
  }

  struct sigaction act;
  sigemptyset(&act.sa_mask);
  act.sa_flags = SA_NODEFER | SA_ONSTACK | SA_RESETHAND;
  act.sa_handler = SIG_DFL;
  sigaction(sig, &act, NULL);
  kill(getpid(),sig);
}

BOOL CGameApplication::init(event_base* evbase){
  struct sigaction act;
  sigemptyset(&act.sa_mask);
  act.sa_flags = SA_NODEFER | SA_ONSTACK | SA_RESETHAND | SA_SIGINFO;
  act.sa_sigaction = sigsegv_handler;
  sigaction(SIGSEGV, &act, NULL);
  return TRUE;
}

void CGameApplication::run(){
    *((uint8_t*)(0x0)) = 1; // segv error test
}

发现以上的代码,在Linux x86下正常,但在一块ARM无法正常打印 查资料得知,ARM的栈回溯需要.ARM.exidx、.ARM.extab两个段,readelf -u可以打印这些信息:

$ readelf -u test

Unwind table index '.ARM.exidx' at offset 0x5c0 contains 3 entries:

0x103f0 <_start>: 0x1 [cantunwind]

0x10518 <main>: @0x105b4
  Compact model index: 1
  0x9b      vsp = r11
  0x40      vsp = vsp - 4
  0x84 0x80 pop {r11, r14}
  0xb0      finish
  0xb0      finish

0x10538 <__libc_csu_init>: 0x1 [cantunwind]

看生成出来的ELF确实是有这些信息的,但打印依旧不正常,当前libunwind版本1.1,尝试升级到1.6.2,待测试是否成功(TODO:待测试)。