【发布时间】:2015-06-16 01:11:18
【问题描述】:
我正在为 Android(仅限 ARM)编写此代码,但我相信通用 Linux 的原理也是相同的。
我正在尝试从信号处理程序中捕获堆栈跟踪,以便在我的应用程序崩溃时记录它。这就是我使用<unwind.h> 想出的。
初始化:
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,并得到了一个奇怪的结果:我得到一个堆栈条目,它是正确的条目 - 首先导致信号的函数。但是它被记录了两次(即使地址相同,所以它不是符号名称查找错误)。显然,这还不够好——我需要整个堆栈。而且我想知道这个结果是否仅仅是偶然的(即它通常不应该起作用。
现在,我读到我还需要提供堆栈指针,我显然可以从ucontext 获得,与 PC 相同。但我不知道该怎么办。我必须手动放松而不是使用_Unwind_Backtrace吗?如果是这样,你能给我示例代码吗?我一直在寻找一天中的大部分时间,但仍然找不到可以复制并粘贴到我的项目中的任何内容。
对于它的价值,这是包含 _Unwind_Backtrace 定义的 libunwind 源。如果我看到它的来源,我想我可以弄清楚一些事情,但它比我预期的要复杂。
【问题讨论】:
-
FWIW,Dalvik VM 从其他线程收集本机堆栈跟踪的方式从这里开始:android.googlesource.com/platform/dalvik/+/kitkat-release/vm/…。该实现正在向其他线程发送信号以使它们进行收集。
-
@fadden:不幸的是,它使用了
corkscrew/backtrace.h,这在 NDK 中不可用,而且我听说该库已从 Android 5 中删除。 -
在相关说明中 - 确保在
CFLAGS中将-fno-omit-frame-pointer传递给编译器以允许跟踪调用图。优化标志(例如-O2)会静默禁用帧指针。 -
@TheCodeArtist:是的,它通过了。正如我已经提到的,即使使用 -O3,我也无法获取调用
dumpBacktrace的线程的堆栈跟踪。 -
这是一个几乎只有标题的库,它能够在出现分段错误的情况下很好地打印出堆栈。 github.com/vmarkovtsev/DeathHandler
标签: c++ linux android-ndk arm backtrace