Могу ли я получить трассировку стека C ++ при сбое приложения Android?

Большинство ошибок, возникающих в моем коде на C ++, заставляют приложение просто выйти, без каких-либо выходных данных LogCat, и на устройстве нет сообщений. Нулевые указатели и неправильное использование JNI часто дают этот результат, и, разумеется, он очень сильно отлаживает отладки.

В настоящее время я могу получить трассировку стека с командой «bt» в ndk-gdb, но не в случае сбоя в течение первых 2 секунд запуска, потому что ndk-gdb запускает процесс и присоединяется к нему после его запуска. Кроме того, ndk-gdb ненадежен, часто говорит, что он не может найти никаких символов или жаловаться на нефатальные ошибки «SIGILL», например.

Есть ли способ уловить ошибку и распечатать трассировку стека или другую информацию при сбое приложения? Например, если есть SIGSEGV, я хотел бы узнать, к какому адресу приложение пыталось получить доступ.

Файл trace.txt дает что-то? Я не помню, было ли его местоположение: /data/anr/trace.txt или /data/data/{pkg}/trace.txt

Вам нужно начать с захвата 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 ); } 

Следующее – вызвать эту функцию для регистрации обработчиков сигналов. Вы можете сделать это как первое в главном, но тогда вы не получите трассировки стека до основного. Если вы хотите их раньше, вы можете вызвать эту функцию из конструктора глобального объекта. Но нет никакой гарантии, что это будет первый вызываемый конструктор. Есть способы убедиться, что он вызван рано. Например, оператор перегрузки 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.

Да, «execinfo.h» там не существует, но CallStack делает:

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

Надеюсь, это поможет в таком обработчике сигналов.