如何展开堆栈以获取指定堆栈指针 (SP) 的回溯? [英] How to unwind the stack to get backtrace for the specified stack pointer (SP)?
问题描述
我是为 Android 编写的(仅限 ARM),但我相信一般 Linux 的原理也是一样的.
I'm writing this for Android (ARM only), but I believe the principle is the same for generic Linux as well.
我正在尝试从信号处理程序中捕获堆栈跟踪,以便在我的应用程序崩溃时记录它.这就是我使用 <unwind.h>
想到的.
初始化:
I'm trying to capture the stack trace from within the signal handler, so that I can log it when my app crashes. This is what I've come up with using <unwind.h>
.
Initialization:
struct sigaction signalhandlerDescriptor;
memset(&signalhandlerDescriptor, 0, sizeof(signalhandlerDescriptor));
signalhandlerDescriptor.sa_flags = SA_SIGINFO;
signalhandlerDescriptor._u._sa_sigaction = signalHandler;
sigaction(SIGSEGV, &signalhandlerDescriptor, 0);
代码本身:
struct BacktraceState
{
void** current;
void** end;
void* pc;
};
inline _Unwind_Reason_Code unwindCallback(struct _Unwind_Context* context, void* arg)
{
BacktraceState* state = static_cast<BacktraceState*>(arg);
state->pc = (void*)_Unwind_GetIP(context);
if (state->pc)
{
if (state->current == state->end)
return _URC_END_OF_STACK;
else
*state->current++ = reinterpret_cast<void*>(state->pc);
}
return _URC_NO_REASON;
}
inline size_t captureBacktrace(void** addrs, size_t max, unsigned long pc)
{
BacktraceState state = {addrs, addrs + max, (void*)pc};
_Unwind_Backtrace(unwindCallback, &state);
personality_routine();
return state.current - addrs;
}
inline void dumpBacktrace(std::ostream& os, void** addrs, size_t count)
{
for (size_t idx = 0; idx < count; ++idx) {
const void* addr = addrs[idx];
const char* symbol = "";
Dl_info info;
if (dladdr(addr, &info) && info.dli_sname) {
symbol = info.dli_sname;
}
int status = -3;
char * demangledName = abi::__cxa_demangle(symbol, 0, 0, &status);
os << "#" << idx << ": " << addr << " " << (status == 0 ? demangledName : symbol) << "\n";
free(demangledName);
}
}
void signalHandler(int sig, siginfo_t *siginfo, void *uctx)
{
ucontext * context = (ucontext*)uctx;
unsigned long PC = context->uc_mcontext.arm_pc;
unsigned long SP = context->uc_mcontext.arm_sp;
Logger() << __PRETTY_FUNCTION__ << "Fatal signal:" << sig;
const size_t maxNumAddresses = 50;
void* addresses[maxNumAddresses];
std::ostringstream oss;
const size_t actualNumAddresses = captureBacktrace(addresses, maxNumAddresses, PC);
dumpBacktrace(oss, addresses, actualNumAddresses);
Logger() << oss.str();
exit(EXIT_FAILURE);
}
问题:如果我通过在 unwindCallback
中调用 _Unwind_GetIP(context)
来获取 PC 寄存器,我会获得信号处理程序堆栈的完整跟踪.这是一个单独的堆栈,这显然不是我想要的.所以我尝试在信号处理程序中提供从 ucontext
中获取的 PC,并得到了一个奇怪的结果:我得到一个堆栈条目,它是正确的条目 - 首先导致信号的函数.但是它被记录了两次(即使地址是相同的,所以它不是一个符号名称查找错误).显然,这还不够好——我需要整个堆栈.我想知道这个结果是否仅仅是偶然的(即它不应该在一般情况下起作用.
Problem: if I get the PC register by calling _Unwind_GetIP(context)
in unwindCallback
, I get the complete trace for the signal handler stack. Which is a separate stack, and that's obviously not what I want. So I tried supplying the PC taken from the ucontext
in signal handler, and got a weird result: I get one stack entry, it is the correct entry - the function which caused the signal in the first place. But it's logged twice (even the address is the same, so it's not a symbolic name look up bug). Obviously, that's not good enough - I need the whole stack. And I wonder if this result is merely accidental (i. e. it shouldn't work in general.
现在,我读到我还需要提供堆栈指针,显然我可以从 ucontext
获得,与 PC 相同.但我不知道该怎么办.我是否必须手动展开而不是使用 _Unwind_Backtrace
?如果是这样,你能给我示例代码吗?我一直在寻找一天中的大部分时间,但仍然找不到可以复制并粘贴到我的项目中的任何内容.
Now, I read I need to also supply the stack pointer, which I apparently can get from ucontext
, same as PC. But I don't know what to do with it. Do I have to unwind manually instead of using _Unwind_Backtrace
? If so, can you give me sample code? I've been searching for the better part of a day, and still couldn't find anything I could copy and paste into my project.
就其价值而言,这是包含 _Unwind_Backtrace
定义的 libunwind 源代码.我以为如果我看到它的来源就可以弄清楚,但它比我预期的要复杂得多.
For what it's worth, here's the libunwind source which contains _Unwind_Backtrace
definition. Thought I could figure something out if I see its source, but it's way more complicated than I expected.
推荐答案
为了得到导致 SIGSEGV 的代码的堆栈跟踪,而不是信号处理程序的堆栈跟踪,您必须从 ucontext_t
并用它们来放松.
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.
但是用_Unwind_Backtrace()
很难做到.因此,如果您使用 libc++ (LLVM STL),最好尝试针对 32 位 ARM 的预编译 libunwind
,与现代 Android NDK 捆绑在一起(位于 sources/cxx-stl/llvm-libc++/libs/armeabi-v7a/libunwind.a
).这是一个示例代码.
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
如果您使用 libstdc++ (GNU STL),请使用 Vasily Galkin 的解决方案:
If you use libstdc++ (GNU STL), use Vasily Galkin's solution:
https://stackoverflow.com/a/30515756/1016580
,这与 Dar Hoo 在另一篇文章中的解决方案相同:
, which is the same as Dar Hoo's solution from another post:
https://stackoverflow.com/a/48593413/1016580
这篇关于如何展开堆栈以获取指定堆栈指针 (SP) 的回溯?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!