12

Most of the errors that occur in my C++ code cause the app to simply exit, with no LogCat output whatsoever, and no message on the device. Null pointers and incorrect use of JNI often produce this result, and needless to say, it makes debugging very hard.

Currently I can get a stack trace with the 'bt' command in ndk-gdb, but not if the crash occurs within the first 2 seconds of startup, because ndk-gdb starts the process and attaches to it after it has started. Plus, ndk-gdb is unreliable, often saying it can't find any symbols, or complaining about nonfatal "SIGILL" errors, for example.

Is there a way to trap the error and print a stack trace, or other information when an app crashes? For example, if there was a SIGSEGV, I'd like to know which address the app was trying to access.

Qwertie
  • 16,354
  • 20
  • 105
  • 148

4 Answers4

4

trace.txt file give something ? I don't remember if his location is : /data/anr/trace.txt or /data/data/{pkg}/trace.txt

xunien
  • 185
  • 1
  • 9
1

You need to start by trapping the SIGSEGV to execute code when you get a segv. This is posix code, so something similar should work on 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 );
}

The next thing is to call that function to register the signal handlers. You can do it as the first thing in main, but then you won't get stack traces until main. If you want them before, you can call this function from the constructor of a global object. But there is no guarantee that it will be the first called constructor. There are ways to make sure it gets called early. For example, overload operator new - in debug builds - to first initialize the stack traces on the first allocation, and then call into the real operator new. This will give you stack traces starting on the first allocation.

To print a stack trace:

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] ):
}

You will need to do more work to demangle the symbols to make them readable. try abi::__cxa_demangle. Of course build with -g and link with -rdynamic.

Rafael Baptista
  • 11,181
  • 5
  • 39
  • 59
  • Thanks. I fixed the bug I was looking for but I'll definitely give this a shot next time. I'm not sure I understand `-rdynamic`, although I looked up the documentation: "Pass the flag -export-dynamic to the ELF linker, on targets that support it. This instructs the linker to add all symbols, not only used ones, to the dynamic symbol table. This option is needed for some uses of dlopen or to allow obtaining backtraces from within a program." (`-g`, meanwhile, "turns on debugging information in the preferred format for the target".) – Qwertie Jun 26 '12 at 22:05
  • 9
    Crap! backtrace is supposed to be in execinfo.h, but it does not exist on Android! (execinfo.h: No such file or directory) – Qwertie Jul 20 '12 at 00:30
  • 3
    Yep, there's no `backtrace`on Android. – Violet Giraffe Jan 23 '14 at 08:44
  • This code is only for glibc/Linux. Under Bionic/Android you can use the libunwind implementation that comes with it. – Raúl Salinas-Monteagudo Feb 23 '18 at 09:41
0

Follow these explanations : https://developer.android.com/ndk/guides/ndk-stack

That is to say:

  1. go to the folder where you have your libnative-lib.so (or other name) file (for me, was : /app/build/intermediates/cxx/RelWithDebInfo/5ww1v5k5/obj/arm64-v8a ); be sure that you choose the convenient folder (that is to say the one corresponding to the architecture of your test device processor)
  2. copy/paste the content of your logcat in a txt file (for example foo.txt) ; be sure that this log contains your crash :) Will look like this:

--------- beginning of crash 2021-12-22 11:01:37.533 7268-11335/? A/libc: Fatal signal 11 (SIGSEGV), code 1 (SEGV_MAPERR), fault addr 0x1 in tid 11335 (Thread-87), pid 7268 etc.... (many lines after that)

  1. place this foo.txt file in the same folder as your libnative-lib.so folder
  2. be sure that you have the path to your NDK in the /my_user_name/.bash_profile file ; something like:

export ANDROID_NDK=/Users/my_user_name/Library/Android/sdk/ndk/22.1.7171670

  1. go back to the folder that contains your architecture folder (..../5ww1v5k5/obj in my case) and type in Terminal:

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

A human-readable stacktrace will be generated in Terminal.

toto_tata
  • 14,526
  • 27
  • 108
  • 198
-2

Yes, 'execinfo.h' doesn't exist there, but CallStack does:

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

Hope it can help in such signal handler.

Vladimir Kunschikov
  • 1,735
  • 1
  • 16
  • 16
  • I must be missing something: fatal error: utils/CallStack.h: No such file or directory #include --- Perhaps there's something else that needs to go into Android.mk or something? – Fellow Traveler Dec 07 '13 at 09:51
  • 3
    There's no file `CallStack.h` in the NDK folder! – Violet Giraffe Jan 23 '14 at 08:44
  • https://android.googlesource.com/platform/system/core.git/+/master/include/utils/CallStack.h https://android.googlesource.com/platform/frameworks/native/+/jb-dev/include/utils/CallStack.h – Vladimir Kunschikov Jan 07 '15 at 15:23
  • I can see that handler was called but CallStack does not print anything to log. How can it be? – fsquirrel Feb 16 '16 at 14:39