'Can I get a C++ stack trace when Android app crashes?
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.
Solution 1:[1]
trace.txt file give something ? I don't remember if his location is : /data/anr/trace.txt or /data/data/{pkg}/trace.txt
Solution 2:[2]
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.
Solution 3:[3]
Follow these explanations : https://developer.android.com/ndk/guides/ndk-stack
That is to say:
- 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)
- 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)
- place this foo.txt file in the same folder as your libnative-lib.so folder
- 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
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.
Solution 4:[4]
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.
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|---|
| Solution 1 | |
| Solution 2 | Rafael Baptista |
| Solution 3 | |
| Solution 4 | Vladimir Kunschikov |
