How to get fullstacktrace using _Unwind_Backtrace on SIGSEGV How to get fullstacktrace using _Unwind_Backtrace on SIGSEGV c c

How to get fullstacktrace using _Unwind_Backtrace on SIGSEGV


You want to backtrace from the signal triggering function, but you backtrace from the signal handler function. That's two different stacks. (Note, the SA_ONSTACK flag in sigaction is irrelevant to your question.)

To find the stack pointer of the of the triggering function, use the third parameter of the handler, i.e. void *rserved. You can reference to the answer in this question: Getting the saved instruction pointer address from a signal handler


If you want to use particularly _Unwind_Context(), you can do it like this (the code is 32-bit ARM specific):


struct BacktraceState {    const ucontext_t*   signal_ucontext;    size_t              address_count = 0;    static const size_t address_count_max = 30;    uintptr_t           addresses[address_count_max] = {};    BacktraceState(const ucontext_t* ucontext) : signal_ucontext(ucontext) {}    bool AddAddress(uintptr_t ip) {        // No more space in the storage. Fail.        if (address_count >= address_count_max)            return false;        // Reset the Thumb bit, if it is set.        const uintptr_t thumb_bit = 1;        ip &= ~thumb_bit;        // Ignore null addresses.        // They sometimes happen when using _Unwind_Backtrace()        // with the compiler optimizations,        // when the Link Register is overwritten by the inner        // stack frames.        if (ip == 0)            return true;        // Ignore duplicate addresses.        // They sometimes happen when using _Unwind_Backtrace()        // with the compiler optimizations,        // because we both add the second address from the Link Register        // in ProcessRegisters() and receive the same address        // in UnwindBacktraceCallback().        if (address_count > 0 && ip == addresses[address_count - 1])            return true;        // Finally add the address to the storage.        addresses[address_count++] = ip;        return true;    }};void ProcessRegisters(        _Unwind_Context* unwind_context, BacktraceState* state) {    assert(state);    assert(unwind_context);    const ucontext_t* signal_ucontext = state->signal_ucontext;    assert(signal_ucontext);    const sigcontext* signal_mcontext = &(signal_ucontext->uc_mcontext);    assert(signal_mcontext);    _Unwind_SetGR(unwind_context, REG_R0,  signal_mcontext->arm_r0);    _Unwind_SetGR(unwind_context, REG_R1,  signal_mcontext->arm_r1);    _Unwind_SetGR(unwind_context, REG_R2,  signal_mcontext->arm_r2);    _Unwind_SetGR(unwind_context, REG_R3,  signal_mcontext->arm_r3);    _Unwind_SetGR(unwind_context, REG_R4,  signal_mcontext->arm_r4);    _Unwind_SetGR(unwind_context, REG_R5,  signal_mcontext->arm_r5);    _Unwind_SetGR(unwind_context, REG_R6,  signal_mcontext->arm_r6);    _Unwind_SetGR(unwind_context, REG_R7,  signal_mcontext->arm_r7);    _Unwind_SetGR(unwind_context, REG_R8,  signal_mcontext->arm_r8);    _Unwind_SetGR(unwind_context, REG_R9,  signal_mcontext->arm_r9);    _Unwind_SetGR(unwind_context, REG_R10, signal_mcontext->arm_r10);    _Unwind_SetGR(unwind_context, REG_R11, signal_mcontext->arm_fp);    _Unwind_SetGR(unwind_context, REG_R12, signal_mcontext->arm_ip);    _Unwind_SetGR(unwind_context, REG_R13, signal_mcontext->arm_sp);    _Unwind_SetGR(unwind_context, REG_R14, signal_mcontext->arm_lr);    _Unwind_SetGR(unwind_context, REG_R15, signal_mcontext->arm_pc);    // Program Counter register aka Instruction Pointer will contain    // the address of the instruction where the crash happened.    // UnwindBacktraceCallback() will not supply us with it.    state->AddAddress(signal_mcontext->arm_pc);    // UnwindBacktraceCallback() does not always supply us with    // the return address of the frame where the crash happened.    // Sometimes Link Register will contain this address    // (noticed when compiling with Clang without optimization),    // but LR may also contain address of some previously visitied frame    // (noticed when compiling with GCC without optimization),    // or LR may contain null address    // (noticed when compiling with Clang with optimization).    // These heuristics are unreliable.#if __clang__    state->AddAddress(signal_mcontext->arm_lr);#endif}_Unwind_Reason_Code UnwindBacktraceCallback(        struct _Unwind_Context* unwind_context, void* state_voidp) {    assert(unwind_context);    assert(state_voidp);    BacktraceState* state = (BacktraceState*)state_voidp;    assert(state);    // On the first UnwindBacktraceCallback() call,    // set registers to _Unwind_Context and BacktraceState.    if (state->address_count == 0) {        ProcessRegisters(unwind_context, state);        return _URC_NO_REASON;    }    uintptr_t ip = _Unwind_GetIP(unwind_context);    bool ok = state->AddAddress(ip);    if (!ok)        return _URC_END_OF_STACK;    return _URC_NO_REASON;}void CaptureBacktrace(BacktraceState* state) {    assert(state);    _Unwind_Backtrace(UnwindBacktraceCallback, state);}void SigActionHandler(int sig, siginfo_t* info, void* ucontext) {    const ucontext_t* signal_ucontext = (const ucontext_t*)ucontext;    assert(signal_ucontext);    BacktraceState backtrace_state(signal_ucontext);    CaptureBacktrace(&backtrace_state);    // Do something with the backtrace - print, save to file, etc.}

But I am advising you to not use _Unwind_Context(), but instead use precompiled libunwind for 32-bit ARM, bundled with modern Android NDKs (at sources/cxx-stl/llvm-libc++/libs/armeabi-v7a/libunwind.a). You will have to use use libc++ (LLVM STL). How to do it, is demonstrated in this my answer:

https://stackoverflow.com/a/50027799/1016580

If you use libstdc++ (GNU STL), use the Dar Hoo's solution:

https://stackoverflow.com/a/48593413/1016580


better you use backtrace and backtrace_symbols_fd to get a stacktrace from a signal handler.