我正试图在我的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函数时,我得到了一个不代表崩溃的奇怪的回溯。
答案 0 :(得分:3)
您可以使用pthread_getattr_np
和pthread_attr_getstack
获取堆栈基址,但您真正需要的是崩溃时的PC和SP。在Linux上,您可以从ucontext
。
如果在配置信号处理程序时设置SA_SIGINFO
标志,则处理函数将获取三个参数而不是一个参数。第三个void*
参数是ucontext
指针。 this question接受的答案解释了一点。
一旦你掌握了所有这些,就可以解开堆叠。如果您不介意超出NDK提供的范围,Android的libcorkscrew具有可以展开堆栈并输出结果的功能。调试器守护程序使用它将本机崩溃转储到日志文件中。
知道debuggerd记录的本机崩溃会在/data/tombstones/
中生成堆栈转储,这可能很有用。文件权限使普通应用程序无法访问它,但在修改后的设备上,您可以将它们拉出来并发送它们。
答案 1 :(得分: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);
}
答案 2 :(得分:0)
为了获得导致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解决方案相同: