我正在开发通过NDK与Android一起工作的本地应用程序。我需要在发生崩溃时调用backtrace()函数。问题是NDK没有<execinfo.h>。有没有其他方法可以追踪到?
backtrace()
<execinfo.h>
goucqfw61#
Android没有backtrace(),但unwind.h在这里提供服务。可以通过dladdr()进行符号化。下面的代码是我简单的回溯实现(没有demangling):
unwind.h
dladdr()
#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 <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";
它可以用于回溯到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());}
#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());
nvbavucw2#
下面是一些可以工作的完整代码,它从尤金Shapovalov的答案开始实现dump_stack(),并在设备上执行符号查找和C++名称解组。该解决方案:
它使用NDK中内置的以下设施:
<unwind.h>
__cxxabiv1::__cxa_demangle()
<cxxabi.h>
到目前为止,我只在一个基于arm的Android 5.1设备上测试了这个功能,并且我只从我的主程序调用它(而不是从信号处理程序)。我使用默认的ndk-build,它为arm平台选择gcc。请评论,如果你能够使这项工作
请注意,r10 e NDK在gcc和clang工具集中都有针对许多架构的<unwind.h>代码,因此支持范围看起来很广。C符号名称分离支持依赖于NDK附带的C STL中的__cxxabiv1::__cxa_demangle()函数。如果您使用GNU STL(Application.mk中的APP_STL := gnustl_static或gnustl_shared;更多信息请参见this page)。如果你目前根本没有使用STL,只需将APP_STL := gnustl_static或gnustl_shared添加到Application.mk。如果您使用STLport,您必须享受一种特殊的乐趣(更多信息见下文)。
Application.mk
APP_STL := gnustl_static
gnustl_shared
**重要提示:**要使此代码工作,您不能使用-fvisibility=hidden gcc编译器选项(至少在调试版本中)。该选项通常用于在发布版本中隐藏符号。
-fvisibility=hidden
很多人都注意到,ndk-build脚本会从NDK .so中剥离符号,同时将其复制到项目的libs/目录中。这是真的(在.so的两个副本上使用nm会产生非常不同的结果),然而,令人惊讶的是,这个特殊的剥离层并没有阻止下面的代码工作。不知何故,即使在剥离后,仍然有符号(只要你记得不要用-fvisibility=hidden编译)。显示为nm -D。关于这个主题的其他帖子讨论了其他编译器选项,如-funwind-tables。我没有发现我需要设置任何这样的选项。默认的ndk-build选项起作用。要使用此代码,请将_my_log()替换为您喜欢的日志记录或字符串函数。STLport用户请参见下面的特别说明。
.so
nm
nm -D
-funwind-tables
_my_log()
#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");}
#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;
if (pc)
if (state->current == state->end)
else
void dump_stack(void)
_my_log("android stack dump");
const int max = 100;
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++)
if (dladdr(addr, &info) && 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");
如果您使用的是STLport STL而不是GNU STL,该怎么办?你是我的,我也是你的。存在两个问题:
cp-demangle.c
cp-demangle.h
demangle/
#define IN_LIBGCC2 1 // means we want to define __cxxabiv1::__cxa_demanglenamespace __cxxabiv1{extern "C"{#include "demangle/cp-demangle.c"}}
#define IN_LIBGCC2 1 // means we want to define __cxxabiv1::__cxa_demangle
namespace __cxxabiv1
extern "C"
#include "demangle/cp-demangle.c"
-I
sources/cxx-stl/gabi++/include/unwind.h
sources/cxx-stl/gabi++/include/unwind.h.NOT
sources/cxx-stl/gabi++/include/unwind-arm.h
sources/cxx-stl/gabi++/include/unwind-arm.h.NOT
sources/cxx-stl/gabi++/include/unwind-itanium.h
sources/cxx-stl/gabi++/include/unwind-itanium.h.NOT
我相信有一些更优雅的解决方案,但是我怀疑切换-I编译器选项的顺序可能会产生其他问题,因为STL通常希望覆盖工具链包含文件。好好享受吧!
33qvvth13#
backtrace()是一个非标准的Glibc扩展,即使在ARM上也有些不稳定(我认为你需要用-funwind-tables构建所有东西,然后有一个新的Glibc?)据我所知,Android使用的Bionic C库中并不包含这个函数。您可以尝试将Glibc回溯的源代码拉到您的项目中,然后使用展开表重新构建有趣的内容,但这对我来说听起来很难。如果你有调试信息,你可以尝试用一个附加到你的进程的脚本来启动GDB,并以这种方式打印一个回溯,但我不知道GDB是否能在Android上工作(尽管Android基本上是Linux,所以很多id很好,安装细节可能有问题?)你可以通过某种方式倾倒核心来获得更远的距离(仿生支持吗?),并在事后对其进行分析。
gt0wga4j4#
这里有一个疯狂的单行方法,可以获得非常详细的堆栈跟踪,包括C/C++(原生)和Java:滥用JNI
env->FindClass(NULL);
只要您的应用程序是编译调试,或以其他方式使用Android的CheckJNI,这个错误的调用将触发Android的内置JNI检查器,这将产生一个华丽的堆栈跟踪控制台(从“艺术”日志源)。这个堆栈跟踪是在Android的libart.so内部完成的,使用了所有最新的技术和铃声,这些技术和铃声对于像我们这样的NDK用户来说并不容易获得。您可以启用CheckJNI,即使是未编译调试的应用程序。请参阅此Google FAQ了解详情。我不知道这个技巧是否适用于SIGSEGV处理程序(从SIGSEGV,您可能会得到错误堆栈的堆栈跟踪,或者可能根本不会触发Art),但值得一试。如果您需要一个解决方案,使堆栈跟踪在您的代码中可用(例如所以你可以通过网络发送或记录它),看到我的另一个答案在这个相同的问题。
libart.so
2hh7jdfx5#
你可以使用CallStack:
#include <utils/CallStack.h>void log_backtrace(){ CallStack cs; cs.update(2); cs.dump();}
#include <utils/CallStack.h>
void log_backtrace()
CallStack cs;
cs.update(2);
cs.dump();
结果将需要通过c++filt或类似的方法进行de-mangling:
c++filt
D/CallStack( 2277): #08 0x0x40b09ac8: <_ZN7android15TimedEventQueue11threadEntryEv>+0x0x40b09961D/CallStack( 2277): #09 0x0x40b09b0c: <_ZN7android15TimedEventQueue13ThreadWrapperEPv>+0x0x40b09af9
D/CallStack( 2277): #08 0x0x40b09ac8: <_ZN7android15TimedEventQueue11threadEntryEv>+0x0x40b09961
D/CallStack( 2277): #09 0x0x40b09b0c: <_ZN7android15TimedEventQueue13ThreadWrapperEPv>+0x0x40b09af9
you@work>$ c++ filt_ZN7android15TimedEventQueue11threadEntryEv_ZN7android15TimedEventQueue13ThreadWrapperEPv
android::TimedEventQueue::threadEntry() android::TimedEventQueue::ThreadWrapper(void*)
android::TimedEventQueue::threadEntry()
android::TimedEventQueue::ThreadWrapper(void*)
eagi6jfj6#
以下是如何使用libunwind在32位ARM上捕获回溯,该工具与现代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);}
// 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;
BacktraceState backtrace_state(signal_ucontext);
CaptureBacktraceUsingLibUnwind(&backtrace_state);
exit(0);
下面是一个示例回溯测试应用程序,其中包含3个实现的回溯方法,包括上面显示的方法。https://github.com/alexeikh/android-ndk-backtrace-test
wr98u20j7#
如果你只想要几个(例如2 - 5个)最上面的调用帧,如果你的GCC足够新,你可以考虑使用一些返回地址或帧地址内置。(But我不太了解Android,所以我可能错了。
owfi6suc8#
Bionic execinfo.h标头自API级别33(Android 14)起已公开,允许您在运行时收集回溯,就像在常规Linux上一样。backtrace的手册页上提供了一个示例:https://man7.org/linux/man-pages/man3/backtrace.3.html对于旧版本的Android,您可以在以下位置重用代码:https://cs.android.com/android/platform/superproject/+/master:bionic/libc/bionic/execinfo.cpp。
execinfo.h
backtrace
8条答案
按热度按时间goucqfw61#
Android没有
backtrace()
,但unwind.h
在这里提供服务。可以通过dladdr()
进行符号化。下面的代码是我简单的回溯实现(没有demangling):
它可以用于回溯到LogCat,如
nvbavucw2#
下面是一些可以工作的完整代码,它从尤金Shapovalov的答案开始实现dump_stack(),并在设备上执行符号查找和C++名称解组。该解决方案:
它使用NDK中内置的以下设施:
<unwind.h>
标头(不是libunwind)dladdr()
__cxxabiv1::__cxa_demangle()
来自<cxxabi.h>
(参见下面的STLport注解)到目前为止,我只在一个基于arm的Android 5.1设备上测试了这个功能,并且我只从我的主程序调用它(而不是从信号处理程序)。我使用默认的ndk-build,它为arm平台选择gcc。
请评论,如果你能够使这项工作
请注意,r10 e NDK在gcc和clang工具集中都有针对许多架构的
<unwind.h>
代码,因此支持范围看起来很广。C符号名称分离支持依赖于NDK附带的C STL中的
__cxxabiv1::__cxa_demangle()
函数。如果您使用GNU STL(Application.mk
中的APP_STL := gnustl_static
或gnustl_shared
;更多信息请参见this page)。如果你目前根本没有使用STL,只需将APP_STL := gnustl_static
或gnustl_shared
添加到Application.mk
。如果您使用STLport,您必须享受一种特殊的乐趣(更多信息见下文)。**重要提示:**要使此代码工作,您不能使用
-fvisibility=hidden
gcc编译器选项(至少在调试版本中)。该选项通常用于在发布版本中隐藏符号。很多人都注意到,ndk-build脚本会从NDK
.so
中剥离符号,同时将其复制到项目的libs/目录中。这是真的(在.so
的两个副本上使用nm
会产生非常不同的结果),然而,令人惊讶的是,这个特殊的剥离层并没有阻止下面的代码工作。不知何故,即使在剥离后,仍然有符号(只要你记得不要用-fvisibility=hidden
编译)。显示为nm -D
。关于这个主题的其他帖子讨论了其他编译器选项,如
-funwind-tables
。我没有发现我需要设置任何这样的选项。默认的ndk-build选项起作用。要使用此代码,请将
_my_log()
替换为您喜欢的日志记录或字符串函数。STLport用户请参见下面的特别说明。
如果您使用的是STLport STL而不是GNU STL,该怎么办?
你是我的,我也是你的。存在两个问题:
<cxxabi.h>
的__cxxabiv1::__cxa_demangle()
调用。您需要从this repository下载两个源文件cp-demangle.c
和cp-demangle.h
,并将它们放在源文件下的demangle/
子目录中,然后执行以下操作而不是#include <cxxabi.h>
:<unwind.h>
类型。你猜对了,STLport中的<unwind.h>
(实际上它在gabi库中,当你选择STLport时,它会出现)是不兼容的。事实上,STLport/gabi includes位于工具链includes之前(请参阅ndk-build输出的-I
选项),这意味着STLport阻止您使用真实的的<unwind.h>
。我找不到任何更好的解决方案,而不是进入并破解我安装的NDK中的文件名:sources/cxx-stl/gabi++/include/unwind.h
至sources/cxx-stl/gabi++/include/unwind.h.NOT
sources/cxx-stl/gabi++/include/unwind-arm.h
至sources/cxx-stl/gabi++/include/unwind-arm.h.NOT
sources/cxx-stl/gabi++/include/unwind-itanium.h
至sources/cxx-stl/gabi++/include/unwind-itanium.h.NOT
我相信有一些更优雅的解决方案,但是我怀疑切换
-I
编译器选项的顺序可能会产生其他问题,因为STL通常希望覆盖工具链包含文件。好好享受吧!
33qvvth13#
backtrace()
是一个非标准的Glibc扩展,即使在ARM上也有些不稳定(我认为你需要用-funwind-tables
构建所有东西,然后有一个新的Glibc?)据我所知,Android使用的Bionic C库中并不包含这个函数。
您可以尝试将Glibc回溯的源代码拉到您的项目中,然后使用展开表重新构建有趣的内容,但这对我来说听起来很难。
如果你有调试信息,你可以尝试用一个附加到你的进程的脚本来启动GDB,并以这种方式打印一个回溯,但我不知道GDB是否能在Android上工作(尽管Android基本上是Linux,所以很多id很好,安装细节可能有问题?)你可以通过某种方式倾倒核心来获得更远的距离(仿生支持吗?),并在事后对其进行分析。
gt0wga4j4#
这里有一个疯狂的单行方法,可以获得非常详细的堆栈跟踪,包括C/C++(原生)和Java:滥用JNI
只要您的应用程序是编译调试,或以其他方式使用Android的CheckJNI,这个错误的调用将触发Android的内置JNI检查器,这将产生一个华丽的堆栈跟踪控制台(从“艺术”日志源)。这个堆栈跟踪是在Android的
libart.so
内部完成的,使用了所有最新的技术和铃声,这些技术和铃声对于像我们这样的NDK用户来说并不容易获得。您可以启用CheckJNI,即使是未编译调试的应用程序。请参阅此Google FAQ了解详情。
我不知道这个技巧是否适用于SIGSEGV处理程序(从SIGSEGV,您可能会得到错误堆栈的堆栈跟踪,或者可能根本不会触发Art),但值得一试。
如果您需要一个解决方案,使堆栈跟踪在您的代码中可用(例如所以你可以通过网络发送或记录它),看到我的另一个答案在这个相同的问题。
2hh7jdfx5#
你可以使用CallStack:
结果将需要通过
c++filt
或类似的方法进行de-mangling:you@work>$ c++ filt_ZN7android15TimedEventQueue11threadEntryEv_ZN7android15TimedEventQueue13ThreadWrapperEPv
eagi6jfj6#
以下是如何使用libunwind在32位ARM上捕获回溯,该工具与现代Android NDK(如NDK r16b)捆绑在一起。
下面是一个示例回溯测试应用程序,其中包含3个实现的回溯方法,包括上面显示的方法。
https://github.com/alexeikh/android-ndk-backtrace-test
wr98u20j7#
如果你只想要几个(例如2 - 5个)最上面的调用帧,如果你的GCC足够新,你可以考虑使用一些返回地址或帧地址内置。
(But我不太了解Android,所以我可能错了。
owfi6suc8#
Bionic
execinfo.h
标头自API级别33(Android 14)起已公开,允许您在运行时收集回溯,就像在常规Linux上一样。backtrace
的手册页上提供了一个示例:https://man7.org/linux/man-pages/man3/backtrace.3.html对于旧版本的Android,您可以在以下位置重用代码:https://cs.android.com/android/platform/superproject/+/master:bionic/libc/bionic/execinfo.cpp。