如何展开堆栈获得回溯指定的堆栈指针(SP)? [英] How to unwind the stack to get backtrace for the specified stack pointer (SP)?

查看:1463
本文介绍了如何展开堆栈获得回溯指定的堆栈指针(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);

在code本身:

The code itself:

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

问题:如果我通过调用 _Unwind_GetIP(上下文) unwindCallback 获得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 ?如果是这样,你可以给我的样品code?我一直在寻找了一天的大部分时间,仍然无法找到任何东西,我可以复制并粘贴到我的项目。

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.

有关它的价值,这里包含 libunwind _Unwind_Backtrace 定义。以为我可以想办法的,如果我看到它的来源,但它比我预想的方式更加复杂。

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.

推荐答案

首先,你需要阅读部分的异步信号安全功能:

First, you need to read the section on "async signal safe" functions:

http://man7.org/linux/man-pages/ man7 / signal.7.html

这就是是安全的信号处理函数调用函数整个集合。关于你能做的最糟糕的事情就是打电话给任何调用引擎盖下的malloc()/ free()的 - 或者自己做。

That's the entire set of functions that are safe to call in a signal handler. About the worst thing you can do is to call anything that calls malloc()/free() under the hood - or do it yourself.

二,搞不定的信号处理程序以外的第一。

Second, get it working outside of a signal handler first.

第三,这些可能是中肯的:

Third, these are probably apropos:

如何获得C ++在Android 回溯

的Andr​​oid NDK:让回溯

这篇关于如何展开堆栈获得回溯指定的堆栈指针(SP)?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆