How to unwind the stack to get backtrace for the specified stack pointer (SP)?

前端 未结 2 1423
离开以前
离开以前 2020-12-08 13:42

I\'m writing this for Android (ARM only), but I believe the principle is the same for generic Linux as well.

I\'m trying to capture the stack trace from within the s

相关标签:
2条回答
  • 2020-12-08 14:26

    In order to to get stacktrace of code which caused SIGSEGV instead of stacktrace of the signal handler, you have to get ARM registers from ucontext_t and use them for unwinding.

    But it is hard to do with _Unwind_Backtrace(). Thus, if you use libc++ (LLVM STL), better try precompiled libunwind for 32-bit ARM, bundled with modern Android NDKs (at sources/cxx-stl/llvm-libc++/libs/armeabi-v7a/libunwind.a). Here is a sample code.


    // This method can only be used on 32-bit ARM with libc++ (LLVM STL).
    // Android NDK r16b contains "libunwind.a" for armeabi-v7a ABI.
    // This library is even silently linked in by the ndk-build,
    // so we don't have to add it manually in "Android.mk".
    // We can use this library, but we need matching headers,
    // namely "libunwind.h" and "__libunwind_config.h".
    // For NDK r16b, the headers can be fetched here:
    // https://android.googlesource.com/platform/external/libunwind_llvm/+/ndk-r16/include/
    #if _LIBCPP_VERSION && __has_include("libunwind.h")
    #include "libunwind.h"
    #endif
    
    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.
            if (ip == 0)
                return true;
    
            // Finally add the address to the storage.
            addresses[address_count++] = ip;
            return true;
        }
    };
    
    void CaptureBacktraceUsingLibUnwind(BacktraceState* state) {
        assert(state);
    
        // Initialize unw_context and unw_cursor.
        unw_context_t unw_context = {};
        unw_getcontext(&unw_context);
        unw_cursor_t  unw_cursor = {};
        unw_init_local(&unw_cursor, &unw_context);
    
        // Get more contexts.
        const ucontext_t* signal_ucontext = state->signal_ucontext;
        assert(signal_ucontext);
        const sigcontext* signal_mcontext = &(signal_ucontext->uc_mcontext);
        assert(signal_mcontext);
    
        // Set registers.
        unw_set_reg(&unw_cursor, UNW_ARM_R0, signal_mcontext->arm_r0);
        unw_set_reg(&unw_cursor, UNW_ARM_R1, signal_mcontext->arm_r1);
        unw_set_reg(&unw_cursor, UNW_ARM_R2, signal_mcontext->arm_r2);
        unw_set_reg(&unw_cursor, UNW_ARM_R3, signal_mcontext->arm_r3);
        unw_set_reg(&unw_cursor, UNW_ARM_R4, signal_mcontext->arm_r4);
        unw_set_reg(&unw_cursor, UNW_ARM_R5, signal_mcontext->arm_r5);
        unw_set_reg(&unw_cursor, UNW_ARM_R6, signal_mcontext->arm_r6);
        unw_set_reg(&unw_cursor, UNW_ARM_R7, signal_mcontext->arm_r7);
        unw_set_reg(&unw_cursor, UNW_ARM_R8, signal_mcontext->arm_r8);
        unw_set_reg(&unw_cursor, UNW_ARM_R9, signal_mcontext->arm_r9);
        unw_set_reg(&unw_cursor, UNW_ARM_R10, signal_mcontext->arm_r10);
        unw_set_reg(&unw_cursor, UNW_ARM_R11, signal_mcontext->arm_fp);
        unw_set_reg(&unw_cursor, UNW_ARM_R12, signal_mcontext->arm_ip);
        unw_set_reg(&unw_cursor, UNW_ARM_R13, signal_mcontext->arm_sp);
        unw_set_reg(&unw_cursor, UNW_ARM_R14, signal_mcontext->arm_lr);
        unw_set_reg(&unw_cursor, UNW_ARM_R15, signal_mcontext->arm_pc);
    
        unw_set_reg(&unw_cursor, UNW_REG_IP, signal_mcontext->arm_pc);
        unw_set_reg(&unw_cursor, UNW_REG_SP, signal_mcontext->arm_sp);
    
        // unw_step() does not return the first IP.
        state->AddAddress(signal_mcontext->arm_pc);
    
        // Unwind frames one by one, going up the frame stack.
        while (unw_step(&unw_cursor) > 0) {
            unw_word_t ip = 0;
            unw_get_reg(&unw_cursor, UNW_REG_IP, &ip);
    
            bool ok = state->AddAddress(ip);
            if (!ok)
                break;
        }
    }
    
    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);
        CaptureBacktraceUsingLibUnwind(&backtrace_state);
        // Do something with the backtrace - print, save to file, etc.
    }
    

    I am also advising to take a look at this my answer which contains more code and more info:

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

    If you use libstdc++ (GNU STL), use Vasily Galkin's solution:

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

    , which is the same as Dar Hoo's solution from another post:

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

    0 讨论(0)
  • 2020-12-08 14:31

    First, you need to read the section on "async signal safe" functions:

    http://man7.org/linux/man-pages/man7/signal.7.html

    That's the entire set of functions that are safe to call in a signal handler. About the worst thing you can do is to call anything that calls malloc()/free() under the hood - or do it yourself.

    Second, get it working outside of a signal handler first.

    Third, these are probably apropos:

    How to get C++ backtrace on Android

    Android NDK: getting the backtrace

    0 讨论(0)
提交回复
热议问题