Android Unwind Backtrace внутри sigaction

Я пытаюсь поймать сигналы, такие как SIGSEGV в моем приложении Android NDK для отладки. Для этого я настроил информацией, что называется.

теперь я пытаюсь получить стек вызова. Проблема в том, что _Unwind_Backtrace работает только с текущим стеком, а sigaction работает внутри собственного стека.

Итак, есть ли способ получить стек указателя выполнения, который получил сигнал? (В основном скажите _Unwind_Backtrace для того чтобы размотать другой стог чем ток?)

Я должен отметить, что :

  • используя backtrace() и backtrace_symbols() не является опцией, так как эти функции не поставляются в Android NDK

  • Я использую GDB для расследования сбоев на моих локальных устройствах. Я не хочу заменять GDB, я хочу иметь возможность получать значимые трассировки стека от клиента при отправке ему тестовой сборки.

изменить: Я пробовал использовать libcorkscrew Android из system / core, Как предложено fadden, но когда я использую функцию unwind_backtrace_signal_arch, я получаю странный backtrace, который не представляет сбой.

3 ответов


вы можете получить базовый адрес стека с pthread_getattr_np и pthread_attr_getstack, но то, что вам действительно нужно, это ПК и SP во время аварии. На Linux, вы можете вытащить их из ucontext.

если вы выберите SA_SIGINFO флаг при настройке обработчика сигнала функция обработчика получает три аргумента вместо одного. Третий


в моей стандартной практикой _Unwind_Backtrace не включили pre-сигнала стека.

мне удалось получить некоторые стеки до сигнала, вызвав внутренний libgcc _ _ gnu_Unwind_Backtrace - у него есть дополнительный инструмент, являющийся "текущими значениями реестра", поэтому он работает с данным стеком, а не с текущим стеком.

//definitions copied from arm-specific libgcc 4.8 sources.
struct core_regs
{
  _uw r[16];
};

typedef struct
{
  _uw demand_save_flags;
  struct core_regs core;
} phase2_vrs;

extern "C"
_Unwind_Reason_Code
__gnu_Unwind_Backtrace(_Unwind_Trace_Fn trace, void * trace_argument,
               phase2_vrs * entry_vrs);

// Getting backtrace with those definitions
//istead of _Unwind_Backtrace(tracer, &state);
if (const ucontext_t* signal_context = last_sigaction_parameter)
{
      phase2_vrs pre_signal_state = {};
      pre_signal_state.core = *reinterpret_cast<const core_regs*>(&(signal_context->uc_mcontext.arm_r0));
      __gnu_Unwind_Backtrace(tracer, &state, &pre_signal_state);
}

чтобы получить stacktrace кода, который вызвал SIGSEGV вместо stacktrace обработчика сигнала, вы должны получить регистры ARM от ucontext_t и использовать их для отдыха.

но это трудно сделать с _Unwind_Backtrace(). Таким образом, если вы используете libc++ (LLVM STL), лучше попробовать precompiled libunwind для 32-битной руки, в комплекте с современными Android NDKs (at sources/cxx-stl/llvm-libc++/libs/armeabi-v7a/libunwind.a). Вот пример кода.


// 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.
}

Я также советую взглянуть на этот мой ответ содержит больше кода и больше информации:

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

если вы используете libstdc++ (GNU STL), используйте решение Василия Галкина:

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

, что совпадает с решением дар Ху из другого поста:

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