【问题标题】:Android _Unwind_Backtrace inside sigactionAndroid _Unwind_Backtrace 里面的 sigaction
【发布时间】:2013-08-03 17:50:40
【问题描述】:

我正在尝试在我的 Android NDK 应用程序中捕获 SIGSEGV 等信号以进行调试。 为此,我设置了一个名为的 sigaction。

我现在正在尝试获取调用堆栈。问题是_Unwind_Backtrace 仅适用于当前堆栈,而 sigaction 在其自己的堆栈中运行。

那么,有没有办法获取接收到信号的执行指针的栈呢? (基本上是告诉_Unwind_Backtrace 展开另一个堆栈而不是当前堆栈?)

我应该指出:

  • 不能使用backtrace()backtrace_symbols(),因为Android NDK 中没有提供这些功能

  • 我正在使用 GDB 调查本地设备上的崩溃。我不想替换 GDB,我希望在向客户端发送测试版本时能够从客户端接收有意义的堆栈跟踪。

编辑: 我曾尝试按照 fadden 的建议从 system/core 使用 Android 的 libcorkscrew,但是当我使用它的 unwind_backtrace_signal_arch 函数时,我得到一个不代表崩溃的奇怪回溯。

【问题讨论】:

标签: android android-ndk signals stack-trace backtrace


【解决方案1】:

你可以用pthread_getattr_nppthread_attr_getstack得到栈基地址,但是你真正需要的是崩溃时的PC和SP。在 Linux 上,您可以将这些从 ucontext 中提取出来。

如果您在配置信号处理程序时设置SA_SIGINFO 标志,您的处理程序函数将获得三个参数而不是一个。第三个void* 参数是一个ucontext 指针。对this question 的接受回答解释了更多。

一旦你得到了所有你可以展开堆栈。如果您不介意超出 NDK 提供的范围,Android 的 libcorkscrew 具有可以展开堆栈并输出结果的功能。 debuggerd 守护进程使用它来将本地崩溃转储到日志文件。

了解调试器记录的本机崩溃在/data/tombstones/ 中生成堆栈转储可能很有用。文件权限使普通应用无法访问它,但在修改过的设备上,您可以将它们拉出并发送。

【讨论】:

  • 我已经搜索过了,但我无法从您的回答中弄清楚如何使用 ucontext 指针展开堆栈。你有什么例子吗?
  • ucontext.h 在仿生中没有提供,这是 android 使用的。我也对如何在不存在该结构的平台上从该结构中提取信息非常感兴趣。我是否应该相信它与 Linux 中的成员排序相同,只需按内存地址取出 PC 和 SP?
  • ucontext.h 是仿生的一部分,但它可能无法从 NDK 获得(请参阅 android.googlesource.com/platform/bionic/+/kitkat-release/libc/… )。并不是说它做得很好——它有一个相当通用的定义。 AFAIK 没有针对 ucontext 的特定于 Android 的自定义。
【解决方案2】:

在我的实践标准中,_Unwind_Backtrace 无法切换到预信号堆栈。

我已经通过调用内部 libgcc __gnu_Unwind_Backtrace 设法获得了一些前信号堆栈 - 它有一个额外的 agrument 是“当前注册表值” - 所以它在给定堆栈上运行,而不是在当前堆栈上运行。

//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);
}

【讨论】:

  • 适用于 GNU STL,但不适用于 LLVM STL。
【解决方案3】:

为了获取导致 SIGSEGV 的代码的堆栈跟踪而不是信号处理程序的堆栈跟踪,您必须从 ucontext_t 获取 ARM 寄存器并将它们用于展开。

但是_Unwind_Backtrace() 很难做到。因此,如果您使用 libc++ (LLVM STL),最好尝试为 32 位 ARM 预编译的 libunwind,与现代 Android NDK 捆绑在一起(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),请使用 Vasily Galkin 的解决方案:

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

,这与 Dar Hoo 在另一篇文章中的解决方案相同:

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多