Android NDK:获取回溯

Android NDK:获取回溯,android,c++,c,android-ndk,Android,C++,C,Android Ndk,我正在开发通过NDK与Android协同工作的本机应用程序。 发生崩溃时,我需要调用backtrace()函数。问题是NDK没有 有没有其他的方法来获得回溯?< P>如果你只需要几个(例如2到5个)最上层的调用框架,如果你的GCC是最近的,你可以考虑使用一些 (但我对Android了解不多,所以我可能错了)backtrace()是一个非标准的Glibc扩展,即使在ARM上也有点不稳定(我认为,你需要用-funwind表构建所有东西,然后有一个新的Glibc?) 据我所知,Android使用的仿生

我正在开发通过NDK与Android协同工作的本机应用程序。 发生崩溃时,我需要调用
backtrace()
函数。问题是NDK没有

有没有其他的方法来获得回溯?

< P>如果你只需要几个(例如2到5个)最上层的调用框架,如果你的GCC是最近的,你可以考虑使用一些

(但我对Android了解不多,所以我可能错了)

backtrace()
是一个非标准的Glibc扩展,即使在ARM上也有点不稳定(我认为,你需要用
-funwind表构建所有东西,然后有一个新的Glibc?)

据我所知,Android使用的仿生C库中不包含此函数

您可以尝试将Glibc backtrace的源代码拉到您的项目中,然后使用unwind表重建有趣的内容,但对我来说这听起来像是一项艰巨的工作


如果您有调试信息,您可以尝试使用附加到进程的脚本启动GDB,并以这种方式打印回溯,但我不知道GDB是否可以在Android上工作(虽然Android基本上是Linux,所以很多id都很好,安装细节可能有问题?),您可以通过某种方式转储core来获得更多信息(仿生支持吗?)并在事后对其进行分析。

您可以使用调用堆栈:

#include <utils/CallStack.h>

void log_backtrace()
{
    CallStack cs;
    cs.update(2);
    cs.dump();
}
you@work>$c++过滤器\u ZN7android15TimedEventQueue11threadEntryEv \u ZN7android15TimedEventQueue13ThreadWrapperEPv

    android::TimedEventQueue::threadEntry()
    android::TimedEventQueue::ThreadWrapper(void*)

Android没有
backtrace()
,但是
unwind.h
在这里提供服务。符号化可以通过
dladdr()
实现

下面的代码是我的回溯的简单实现(没有Demanling):

#包括
#包括
#包括
#包括
名称空间{
结构回溯属性
{
无效**电流;
无效**结束;
};
静态_展开_原因_代码展开回调(结构_展开_上下文*Context,void*arg)
{
BacktraceState*状态=静态(arg);
uintpttr\u t pc=\u Unwind\u GetIP(上下文);
中频(pc){
如果(状态->当前==状态->结束){
返回\u堆栈的\u URC\u END\u;
}否则{
*状态->当前++=重新解释(pc);
}
}
返回(来源)无原因;;
}
}
大小\u t captureBacktrace(无效**缓冲区,大小\u t最大值)
{
BacktraceState状态={buffer,buffer+max};
_展开回溯(展开回调和状态);
返回状态。当前-缓冲区;
}
void dumpBacktrace(标准::ostream&os,void**缓冲区,大小\u t计数)
{
对于(大小\u t idx=0;idxOS

这里有一些工作和完整的代码,它通过Eugene Shapovalov的回答开始执行DimpHistAck(),并在设备上进行符号查找和C++名称分拆。

  • 与NDK r10e配合使用(您不需要完整的Android AOSP源代码树)
  • 不需要任何额外的第三方库(无libunwind、libbacktrace、corkswill、CallStack)
  • 不依赖于设备上安装的任何共享库(例如,在Android 5中被取消的corkscrew)
  • 不会强制您将地址映射到开发机器上的符号;所有符号名称都会在您的代码中显示在Android设备上
它使用NDK内置的这些设施:

  • NDK工具链/dirs中的
    标题(非libunwind)
  • dladdr()
  • \uuucxaxabiv1::\uuucxa\u demangle()
    来自
    (请参见下面的STLport注释)
到目前为止,我只在基于arm的Android 5.1设备上进行了测试,我只在我的主程序(而不是信号处理器)中调用了它。我使用的是默认的ndk构建,它为arm平台选择了gcc

请评论,如果你能使这项工作

  • 在其他Android操作系统上
  • 来自崩溃时的SIGSEGV处理程序(我的目标只是在断言失败时打印堆栈跟踪)
  • 使用clang工具集而不是gcc
注意,r10e NDK在gcc和clang工具集中都有许多体系结构的
代码,因此支持范围很广

<> > C++符号名解散支持取决于一个C++代码,该代码来自于NDK中包含的C++ STL。(
APP_STL:=gnustl_static
gnustl_shared
中的
Application.mk
;有关更多信息,请参阅)。如果您当前根本不使用STL,只需将
APP_STL:=gnustl_static
gnustl_shared
添加到
Application.mk
。如果您使用STL端口,您必须享受一种特殊的乐趣(详见下文)

重要提示:要使此代码正常工作,您不得使用
-fvisibility=hidden
gcc编译器选项(至少在调试版本中)。该选项通常用于在发布版本中隐藏符号,以防窥探

许多人注意到,ndk构建脚本在将ndk
复制到项目的libs/目录时会从ndk
中剥离符号。这是真的(在
的两个副本上使用
nm
。因此
会产生非常不同的结果)然而,令人惊讶的是,这个特殊的剥离层并没有阻止下面的代码工作。即使在剥离之后,仍然有一些符号(只要你记得不要使用
-fvisibility=hidden
编译)。它们显示为
nm-D

关于此主题的其他帖子讨论了其他编译器选项,如
-funwind tables
。我没有发现需要设置任何此类选项。默认的ndk构建选项有效

要使用此代码,请替换
\u my\u log()    android::TimedEventQueue::threadEntry()
    android::TimedEventQueue::ThreadWrapper(void*)
#include <iostream>
#include <iomanip>

#include <unwind.h>
#include <dlfcn.h>

namespace {

struct BacktraceState
{
    void** current;
    void** end;
};

static _Unwind_Reason_Code unwindCallback(struct _Unwind_Context* context, void* arg)
{
    BacktraceState* state = static_cast<BacktraceState*>(arg);
    uintptr_t pc = _Unwind_GetIP(context);
    if (pc) {
        if (state->current == state->end) {
            return _URC_END_OF_STACK;
        } else {
            *state->current++ = reinterpret_cast<void*>(pc);
        }
    }
    return _URC_NO_REASON;
}

}

size_t captureBacktrace(void** buffer, size_t max)
{
    BacktraceState state = {buffer, buffer + max};
    _Unwind_Backtrace(unwindCallback, &state);

    return state.current - buffer;
}

void dumpBacktrace(std::ostream& os, void** buffer, size_t count)
{
    for (size_t idx = 0; idx < count; ++idx) {
        const void* addr = buffer[idx];
        const char* symbol = "";

        Dl_info info;
        if (dladdr(addr, &info) && info.dli_sname) {
            symbol = info.dli_sname;
        }

        os << "  #" << std::setw(2) << idx << ": " << addr << "  " << symbol << "\n";
    }
}
#include <sstream>
#include <android/log.h>

void backtraceToLogcat()
{
    const size_t max = 30;
    void* buffer[max];
    std::ostringstream oss;

    dumpBacktrace(oss, buffer, captureBacktrace(buffer, max));

    __android_log_print(ANDROID_LOG_INFO, "app_name", "%s", oss.str().c_str());
}
#include <unwind.h>
#include <dlfcn.h>
#include <cxxabi.h>

struct android_backtrace_state
{
    void **current;
    void **end;
};

_Unwind_Reason_Code android_unwind_callback(struct _Unwind_Context* context, 
                                            void* arg)
{
    android_backtrace_state* state = (android_backtrace_state *)arg;
    uintptr_t pc = _Unwind_GetIP(context);
    if (pc) 
    {
        if (state->current == state->end) 
        {
            return _URC_END_OF_STACK;
        } 
        else 
        {
            *state->current++ = reinterpret_cast<void*>(pc);
        }
    }
    return _URC_NO_REASON;
}

void dump_stack(void)
{
    _my_log("android stack dump");

    const int max = 100;
    void* buffer[max];

    android_backtrace_state state;
    state.current = buffer;
    state.end = buffer + max;

    _Unwind_Backtrace(android_unwind_callback, &state);

    int count = (int)(state.current - buffer);

    for (int idx = 0; idx < count; idx++) 
    {
        const void* addr = buffer[idx];
        const char* symbol = "";

        Dl_info info;
        if (dladdr(addr, &info) && info.dli_sname) 
        {
            symbol = info.dli_sname;
        }
        int status = 0; 
        char *demangled = __cxxabiv1::__cxa_demangle(symbol, 0, 0, &status); 

        _my_log("%03d: 0x%p %s",
                idx,
                addr,
                (NULL != demangled && 0 == status) ?
                demangled : symbol);

        if (NULL != demangled)
            free(demangled);        
    }

    _my_log("android stack dump done");
}
#define IN_LIBGCC2 1 // means we want to define __cxxabiv1::__cxa_demangle
namespace __cxxabiv1
{
extern "C"
{
#include "demangle/cp-demangle.c"
}
}
env->FindClass(NULL);
// 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/
#include "libunwind.h"

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;

        // 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,
    // the address of the instruction which caused the crash.
    // Thus let's add this address manually.
    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);

    exit(0);
}