giv*_*ivi 44 c c++ android android-ndk
我正在开发通过NDK与Android一起使用的本机应用程序.我需要在backtrace()发生崩溃时调用该函数.问题是<execinfo.h>NDK 没有 .
有没有其他方法来获得回溯?
Eug*_*lov 38
Android没有backtrace(),但是unwind.h在这里服务.符号化可以通过dladdr().
以下代码是我的回溯简单实现(没有demangling):
#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";
}
}
Run Code Online (Sandbox Code Playgroud)
它可以用于回溯到LogCat之类的
#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());
}
Run Code Online (Sandbox Code Playgroud)
Lou*_*ini 21
下面是一些工作和完整的代码,它们通过从Eugene Shapovalov的答案开始实现dump_stack(),并在设备上执行符号查找和C++名称解码.此解决方案:
它使用NDK中内置的这些功能:
<unwind.h> NDK工具链/目录中的标题(不是libunwind)dladdr()__cxxabiv1::__cxa_demangle()来自<cxxabi.h>(见下面的STLport备注)到目前为止,我只使用基于arm的Android 5.1设备测试了这个,我只从我的主程序(而不是信号处理程序)调用它.我正在使用默认的ndk-build,为arm平台选择gcc.
如果你能够做这项工作,请评论
请注意,r10e NDK具有<unwind.h>gcc和clang工具集中许多体系结构的代码,因此支持看起来很广泛.
C++符号名称demangling支持取决于__cxxabiv1::__cxa_demangle()来自NDK附带的C++ STL 的函数.如果您使用GNU STL进行Android构建(APP_STL := gnustl_static或者gnustl_shared在Application.mk;请参阅此页面以获取更多信息),这应该可以正常工作.如果您在使用currrently没有STL可言,只需添加APP_STL := gnustl_static或gnustl_shared到Application.mk.如果您使用STLport,您必须享受特殊的乐趣(更多信息如下).
重要信息:要使此代码生效,您不能使用-fvisibility=hiddengcc编译器选项(至少在您的调试版本中).该选项通常用于在发布版本中隐藏符号以防止窥探.
很多人都注意到ndk-build脚本会从NDK中删除符号,.so同时将其复制到项目的libs /目录中.这是真的(使用nm两个副本.so给出非常不同的结果)然而,这个特殊的剥离层令人惊讶地不会阻止下面的代码工作.不知怎的,甚至在剥离之后仍然有符号(只要你记得不要编译-fvisibility=hidden).他们出现了nm -D.
关于该主题的其他帖子讨论了其他编译器选项-funwind-tables.我没有发现我需要设置任何这样的选项.默认的ndk-build选项有效.
要使用此代码,请_my_log()使用您喜欢的日志记录或字符串函数替换.
STLport用户可以在下面看到特别说明.
#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");
}
Run Code Online (Sandbox Code Playgroud)
如果您使用STLport STL而不是GNU STL怎么办?
很难成为你(和我).有两个问题:
第一个问题是STLport缺少__cxxabiv1::__cxa_demangle()来自的调用<cxxabi.h>.您需要下载两个源文件cp-demangle.c并cp-demangle.h从此存储库中将它们放在demangle/源代码下的子目录中,然后执行以下操作而不是#include <cxxabi.h>:
#define IN_LIBGCC2 1 // means we want to define __cxxabiv1::__cxa_demangle
namespace __cxxabiv1
{
extern "C"
{
#include "demangle/cp-demangle.c"
}
}
Run Code Online (Sandbox Code Playgroud)第二个问题更令人讨厌.事实证明,<unwind.h>NDK中不存在一种,而不是两种,而是三种不同的,不兼容的类型.而且你猜对了<unwind.h>,STLport(实际上是在你选择STLport的时候出现在gabi ++库中)是不兼容的.STLport/gabi ++包含在工具链包含之前的事实(参见你的ndk-build输出-I选项)意味着STLport阻止你使用真实的<unwind.h>.我找不到更好的解决方案,而不是进入并破解我安装的NDK中的文件名:
sources/cxx-stl/gabi++/include/unwind.h 至 sources/cxx-stl/gabi++/include/unwind.h.NOTsources/cxx-stl/gabi++/include/unwind-arm.h 至 sources/cxx-stl/gabi++/include/unwind-arm.h.NOTsources/cxx-stl/gabi++/include/unwind-itanium.h 至 sources/cxx-stl/gabi++/include/unwind-itanium.h.NOT我确信有一些更优雅的解决方案,但我怀疑切换-I编译器选项的顺序可能会产生其他问题,因为STL通常想要覆盖工具链包含文件.
请享用!
ams*_*ams 20
backtrace()是一个非标准的Glibc扩展,甚至在ARM上有些不稳定(你需要用一些东西构建-funwind-tables,我想,然后有一个新的Glibc?)
据我所知,这个功能不包含在Android使用的Bionic C库中.
您可以尝试将Glibc回溯源添加到项目中,然后使用展开表重建有趣的东西,但这对我来说听起来很辛苦.
如果您有调试信息,您可以尝试使用附加到您的进程的脚本启动GDB,并以这种方式打印回溯,但我不知道GDB是否适用于Android(尽管Android基本上是Linux,所以很好,安装细节可能有问题吗?)你可能会通过某种方式倾倒核心(Bionic支持那个吗?)并在事后进行分析.
您可以使用CallStack:
#include <utils/CallStack.h>
void log_backtrace()
{
CallStack cs;
cs.update(2);
cs.dump();
}
Run Code Online (Sandbox Code Playgroud)
结果将需要通过c++filt或类似的去除:
D/CallStack( 2277): #08 0x0x40b09ac8: <_ZN7android15TimedEventQueue11threadEntryEv>+0x0x40b09961
D/CallStack( 2277): #09 0x0x40b09b0c: <_ZN7android15TimedEventQueue13ThreadWrapperEPv>+0x0x40b09af9
Run Code Online (Sandbox Code Playgroud)
你@ work> $ c ++ filt _ZN7android15TimedEventQueue11threadEntryEv _ZN7android15TimedEventQueue13ThreadWrapperEPv
android::TimedEventQueue::threadEntry()
android::TimedEventQueue::ThreadWrapper(void*)
Run Code Online (Sandbox Code Playgroud)
这是一个疯狂的单行方法,用于获取包含C/C++(本机)和Java的滥用JNI的非常详细的堆栈跟踪:滥用JNI
env->FindClass(NULL);
Run Code Online (Sandbox Code Playgroud)
只要您的应用程序被编译调试,或以其他方式使用Android的CheckJNI,这种错误的通话将触发Android的内置JNI检查,这将产生在控制台上一个华丽的堆栈跟踪(从"艺术"的日志源).这个堆栈跟踪是在Android内部完成的,libart.so使用了所有最新技术以及那些像我们这样的低端NDK用户无法轻易获得的铃声和口哨声.
即使是未编译调试的应用程序,也可以启用CheckJNI.有关详情,请参阅此Google常见问题解答
我不知道这个技巧是否适用于SIGSEGV处理程序(来自SIGSEGV,您可能会得到错误堆栈的堆栈跟踪,或者根本不会触发艺术),但值得一试.
如果您需要一个能够在您的代码中使用堆栈跟踪的解决方案(例如,您可以通过网络发送或记录它),请在同一问题中查看我的其他答案.
以下是如何使用 libunwind 在 32 位 ARM 上捕获回溯,libunwind 与现代 Android NDK(例如 NDK r16b)捆绑在一起。
// 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);
}
Run Code Online (Sandbox Code Playgroud)
这是一个示例回溯测试应用程序,其中包含 3 种实现的回溯方法,包括上面所示的方法。
https://github.com/alexeikh/android-ndk-backtrace-test