segv を取得したときにコードを実行するには、SIGSEGV をトラップすることから始める必要があります。これは 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 になるまでスタック トレースを取得できません。以前にそれらが必要な場合は、グローバル オブジェクトのコンストラクターからこの関数を呼び出すことができます。ただし、それが最初に呼び出されるコンストラクターになるという保証はありません。確実に早期に呼び出されるようにする方法があります。たとえば、デバッグ ビルドでは new 演算子をオーバーロードして、最初の割り当てで最初にスタック トレースを初期化し、次に実際の演算子 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 でリンクします。