LoginSignup
20
21

More than 5 years have passed since last update.

【Android NDK】sigsegvをフックする【cocos2d-x】

Last updated at Posted at 2014-03-25

cocos2d-xのAndroidではネイティブの処理を利用しているため、ハイパフォーマンスな動作が期待出来ます。
が、実際にアプリを開発しているとエラーに直面することも多いと思います。
その際に出るエラーはこのようなものです。

I/DEBUG   ( xxxx): *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
I/DEBUG   ( xxxx): Build fingerprint: '---------------------------------------'
I/DEBUG   ( xxxx): pid: xxxx, tid: xxxx  >>> /system/bin/netd <<<
I/DEBUG   ( xxxx): signal 11 (SIGSEGV), fault addr deadbaad
I/DEBUG   ( xxxx):  r0 00001728  r1 100ffd7c  r2 00000027  r3 00000000
I/DEBUG   ( xxxx):  r4 afd42328  r5 00000000  r6 00000000  r7 00011000
I/DEBUG   ( xxxx):  r8 00100000  r9 aef01cad  10 10000000  fp 40008008
I/DEBUG   ( xxxx):  ip ffffffff  sp 100ffd68  lr deadbaad  pc afd11ca4  cpsr 40000030
以下略

デバッグダンプとか呼ばれているもので、ここからエラーの原因を追っていくわけですが、
このエラー、AndroidOSが発しているものなので、フックすることが出来ます。

Test.cpp

#import <stdio.h>
#import <stdlib.h>
#import <android/log.h>
#import <signal.h>

void initSignalHandler(){
    signal(SIGSEGV , &sigsegvSignalHandler);
}

static void sigsegvSignalHandler(int sig){
        jclass exception;
        signal(sig , SIG_IGN);
        JNIEnv *pEnv = 0;
        bool bRet = false;
        JavaVM* jvm = cocos2d::JniHelper::getJavaVM();
        jvm->GetEnv((void**)&pEnv , JNI_VERSION_1_4);
        if (! pEnv)
        {
            return;
        }

        exception = getClassID(pEnv , "java.lang.RuntimeException");
        if(exception != NULL){
             jmethodID methodId = pEnv->GetMethodID(exception , "toString" , "()Ljava/lang/String;");
             jmethodID methodPrintStack = pEnv->GetMethodID(exception , "printStackTrace" , "()V");

             jstring errMes = (jstring)pEnv->CallObjectMethod(exception , methodId);
             pEnv->CallObjectMethod(exception , methodPrintStack);
             const char* chars = pEnv->GetStringUTFChars(errMes , NULL);
             pEnv->ThrowNew(exception , chars);
             pEnv->DeleteLocalRef(exception);
             free((void*)chars);
             pEnv->DeleteLocalRef(chars);
       }
    }

signal関数を使用して、そこにHandlerを登録すると、
sigsegvが発生した時に処理を受けることが出来ます。
そこからjavaのRuntimeExceptionを生成して、それをthrowすることも出来ます。

initSignalHandlerは好きなタイミングで呼び出して下さい。
アプリ起動時にJNI経由で呼び出すなら、こんな感じでしょうか。

MainActivity.java

static private native void initSignalHandler();

@Override
protected void onCreate(Bundle savedInstanceState) {
     super.onCreate(savedInstanceState);
     initSignalHandler();
}
Test.h

#ifdef __cplusplus
extern "C" {
#endif
     JNIEXPORT void JNICALL Java_hoge_hoge_MainActivity_initSignalHandler(JNIEnv *, jclass);
#ifdef __cplusplus
}
#endif
Test.cpp
JNIEXPORT void JNICALL Java_hoge_hoge_MainActivity_initSignalHandler(JNIEnv* env, jclass clazz) {
     signal(SIGSEGV , &sigsegvSignalHandler);
}

で、

で!

で!!!!!

RuntimeExceptionの中身は…空っぽです。
なんにもありません。
そりゃそうだ、何も入れてないんだから!

このソースはこのままでは使えないので、利用方法によって変えるといいよ!
(ログ収集SDK組み込んでるならlogEvent仕込んだりね)

と、投げっぱなしジャーマンな事だったんで、書きにくかったんですが、
途中経過って事で何卒ご了承を。

本当はsigsegvが吐き出すデバッグダンプの中身まで拾いたい。

【追記】
import忘れてた…。

20
21
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
20
21