11

我的 C++ 代码中发生的大多数错误都会导致应用程序简单地退出,没有任何 LogCat 输出,设备上也没有消息。空指针和错误使用 JNI 经常会产生这种结果,不用说,它使调试变得非常困难。

目前我可以在 ndk-gdb 中使用“bt”命令获取堆栈跟踪,但如果崩溃发生在启动的前 2 秒内,则不能,因为 ndk-gdb 启动进程并在启动后附加到它。另外,ndk-gdb 不可靠,经常说它找不到任何符号,或者抱怨非致命的“SIGILL”错误,例如。

有没有办法在应用程序崩溃时捕获错误并打印堆栈跟踪或其他信息?例如,如果有一个 SIGSEGV,我想知道应用程序试图访问哪个地址。

4

4 回答 4

4

trace.txt文件给点东西?我不记得他的位置是:/data/anr/trace.txt还是/data/data/{pkg}/trace.txt

于 2012-06-25T18:50:11.350 回答
1

您需要首先捕获 SIGSEGV 以在获得 segv 时执行代码。这是posix代码,所以类似的东西应该适用于android:

void abortHandler( int signum, siginfo_t* si, void* unused )
{
   const char* name = NULL;
   switch( signum )
   {
   case SIGABRT: name = "SIGABRT";  break;
   case SIGSEGV: name = "SIGSEGV";  break;
   case SIGBUS:  name = "SIGBUS";   break;
   case SIGILL:  name = "SIGILL";   break;
   case SIGFPE:  name = "SIGFPE";   break;
   case SIGPIPE: name = "SIGPIPE";  break;
   }

   if ( name )
      printf( stderr, "Caught signal %d (%s)\n", signum, name );
   else 
      printf( stderr, "Caught signal %d\n", signum );

   printStackTrace( stderr );

   exit( signum );
}

void handleCrashes()
{
   struct sigaction sa;
   sa.sa_flags = SA_SIGINFO;
   sa.sa_sigaction = abortHandler;
   sigemptyset( &sa.sa_mask );

   sigaction( SIGABRT, &sa, NULL );
   sigaction( SIGSEGV, &sa, NULL );
   sigaction( SIGBUS,  &sa, NULL );
   sigaction( SIGILL,  &sa, NULL );
   sigaction( SIGFPE,  &sa, NULL );
   sigaction( SIGPIPE, &sa, NULL );
}

接下来是调用该函数来注册信号处理程序。您可以将其作为 main 中的第一件事,但在 main 之前您不会获得堆栈跟踪。如果你以前想要它们,你可以从全局对象的构造函数中调用这个函数。但是不能保证它会是第一个被调用的构造函数。有一些方法可以确保它被尽早调用。例如,重载 operator new - 在调试版本中 - 首先在第一次分配时初始化堆栈跟踪,然后调用真正的 operator new。这将为您提供从第一次分配开始的堆栈跟踪。

要打印堆栈跟踪:

void printStackTrace( unsigned int max_frames = 63 )
{
   void* addrlist[max_frames+1];

   // retrieve current stack addresses
   u32 addrlen = backtrace( addrlist, sizeof( addrlist ) / sizeof( void* ));

   if ( addrlen == 0 ) 
   {
      printf( stderr, "  <empty, possibly corrupt>\n" );
      return;
   }

   char** symbollist = backtrace_symbols( addrlist, addrlen );

   for ( u32 i = 3; i < addrlen; i++ )
      printf( stderr, "%s\n", symbollist[i] ):
}

您将需要做更多的工作来解开符号以使其可读。试试 abi::__cxa_demangle。当然使用 -g 构建并使用 -rdynamic 链接。

于 2012-06-25T19:10:56.770 回答
0

请遵循以下说明:https ://developer.android.com/ndk/guides/ndk-stack

也就是说:

  1. 转到您拥有 libnative-lib.so (或其他名称)文件的文件夹(对我来说,是: /app/build/intermediates/cxx/RelWithDebInfo/5ww1v5k5/obj/arm64-v8a );确保您选择了方便的文件夹(即与您的测试设备处理器的架构相对应的文件夹)
  2. 将 logcat 的内容复制/粘贴到 txt 文件中(例如 foo.txt);确保此日志包含您的崩溃 :) 将如下所示:

--------- 崩溃开始 2021-12-22 11:01:37.533 7268-11335/? A/libc:致命信号 11 (SIGSEGV)、代码 1 (SEGV_MAPERR)、tid 11335 (Thread-87) 中的故障地址 0x1、pid 7268 等....(之后的许多行)

  1. 将此 foo.txt 文件放在与 libnative-lib.so 文件夹相同的文件夹中
  2. 确保在 /my_user_name/.bash_profile 文件中有 NDK 的路径;就像是:

导出 ANDROID_NDK=/Users/my_user_name/Library/Android/sdk/ndk/22.1.7171670

  1. 返回包含您的架构文件夹的文件夹(在我的例子中是..../5ww1v5k5/obj)并输入终端:

    $ANDROID_NDK/ndk-stack -sym arm64-v8a -dump arm64-v8a/foo.txt

将在终端中生成人类可读的堆栈跟踪。

于 2021-12-22T12:18:08.977 回答
-2

是的,'execinfo.h' 在那里不存在,但 CallStack 存在:

#include <utils/CallStack.h>
..
CallStack cs;
cs.dump();

希望它可以帮助这样的信号处理程序。

于 2013-09-30T08:05:46.073 回答