JNI 异常捕获

update:

以下的代码不是很好用, 只是demo性质.
我目前用的是现成的框架: 爱奇艺的xCrash
可以setNativeCallback
有兴趣的可以研究此项目的代码

deprecated:

实例代码:
(待优化点是把GetStaticMethodID等反射得到的结果缓存起来, 因为反射的方法比较耗资源,
所以在Crash时直接用缓存的数据, 能增加代码的健壮性)

#include 
#include 
#include "Log.h"
#include 
#include 
#include 
#define TAG "faceVeriSys"
#define Method(x) Java_com_xxx_jni_JniInterface_##x
using namespace std;

JavaVM* g_jvm;
// 定义代码跳转锚点
sigjmp_buf JUMP_ANCHOR;
string exceptionSummary = "";

void callJavaNotifyNativeException() {
    LOGE("JniInterfacexx", "callJavaNotifyNativeException start1")
    bool hasDoAttach = false;
    do {
        LOGE(TAG, "callJavaNotifyNativeException start2")
        JNIEnv *env;
        LOGE(TAG, "callJavaNotifyNativeException start3")
        // check it's all ok
        if (g_jvm == NULL) {
            LOGE(TAG, "callJavaNotifyNativeException start4")
            LOGE("JniInterfacexx", "g_jvm == NULL");
            break;
        }
        LOGE(TAG, "callJavaNotifyNativeException start5")
        int getEnvStat = g_jvm->GetEnv((void **) &env, JNI_VERSION_1_6);
        LOGE(TAG, "callJavaNotifyNativeException start6")
        if (getEnvStat == JNI_EDETACHED) {
            LOGE(TAG, "callJavaNotifyNativeException start7")
            int attachtatus = g_jvm->AttachCurrentThread(&env, NULL);
            if (attachtatus != 0) {
                LOGE(TAG, "callJavaNotifyNativeException start8")
                LOGE("JniInterfacexx", "GetEnv Failed to attach")
                break;
            }
            LOGE(TAG, "callJavaNotifyNativeException start9")
            hasDoAttach = true;
        } else if (getEnvStat == JNI_EVERSION) {
            LOGE("JniInterfacexx", "GetEnv JNI version not supported")
            LOGE(TAG, "callJavaNotifyNativeException start10")
            break;
        }
        LOGE("JniInterfacexx", "callJavaNotifyNativeException start11")
        jclass crashHandlerClass = env->FindClass(
                "com/ulsee/rk3288/verification/jni/JniInterface");
        LOGE("JniInterfacexx", "callJavaNotifyNativeException start12")
        jmethodID notifyNativeCrash = env->GetStaticMethodID(crashHandlerClass,
                                                             "notifyNativeException", "(Ljava/lang/String;)V");
        LOGE("JniInterfacexx", "callJavaNotifyNativeException start13");
        env->CallStaticVoidMethod(crashHandlerClass, notifyNativeCrash, env->NewStringUTF(exceptionSummary.c_str()));
        LOGE("JniInterfacexx", "callJavaNotifyNativeException start14");
    } while (false);
    LOGE("JniInterfacexx", "callJavaNotifyNativeException start15");
    if (hasDoAttach) {
        g_jvm->DetachCurrentThread();
    }
}

void exception_sigaction(int signal, siginfo_t *info, void *reserved) {
    LOGE(TAG, "exception_sigaction");
    std::string exceptionSummary2 = "JNI_ERROR,signal: " + to_string(signal) + ", number: " + to_string(info->si_signo) + ", code: " + to_string(info->si_code) + ", error: " + to_string(info->si_errno);

    if (strcmp(exceptionSummary.c_str(),"") != 0){
        //如果已经执行过exception_sigaction
        //LOGE(TAG, "strcmp(exceptionSummary.c_str(),exceptionSummary2.c_str()) ==0 %d", strcmp(exceptionSummary.c_str(),exceptionSummary2.c_str()));
            return;
        } else{
        LOGE(TAG, "strcmp(exceptionSummary.c_str(), "") == %d", strcmp(exceptionSummary.c_str(),""));
    }
    LOGE(TAG, "exceptionSummary2 %s", exceptionSummary2.c_str());
    exceptionSummary = exceptionSummary2;
    siglongjmp(JUMP_ANCHOR, 1);
}

extern "C"
{

#define Mega (1024*1024)
#define Giga (1024*Mega)
JNIEXPORT jint JNICALL
Method(initJniCrashHandler)(JNIEnv *env, jclass type) {
   LOGE(TAG,"initJniCrashHandler");
    env->GetJavaVM(&g_jvm);
    if (!sigsetjmp(JUMP_ANCHOR, 1)) {
        LOGE(TAG,"sigsetjmp success"); //第一次执行sigsetjmp,返回0
    } else {
        LOGE(TAG,"sigsetjmp from jni error"); //第一次执行sigsetjmp,返回0
        // 从siglongjmp跳转sigsetjmp所在行,sigsetjmp返回1,因此进入此行
        callJavaNotifyNativeException();
        return -1;
    }
    struct sigaction sigact; // 注册要捕捉的系统信号量
    //sigset_t block_mask;
    //sigemptyset(&block_mask);
    //sigaddset(&block_mask, SIGABRT); // handler处理捕捉到的信号量时,需要阻塞的信号
    //sigaddset(&block_mask, SIGSEGV); // handler处理捕捉到的信号量时,需要阻塞的信号
    // sigemptyset(&sigact.sa_mask);
    sigact.sa_flags = SA_SIGINFO | SA_ONSTACK;
    //sigact.sa_mask = block_mask;
    sigact.sa_sigaction = exception_sigaction;
    sigaction(SIGABRT, &sigact, NULL); // Abort 异常终止条件,例如 abort() 所起始的
    sigaction(SIGSEGV, &sigact, NULL); // Segmentation violation 非法内存访问
    sigaction(SIGILL, &sigact, NULL); // Illegal instruction 非法指令
    sigaction(SIGBUS, &sigact, NULL); // BUS error 硬件访问异常
    sigaction(SIGFPE, &sigact, NULL); // Floating-point exception 错误的算术运算,如除以零,并非一定要涉及浮点算术
    sigaction(SIGPIPE, &sigact, NULL); // Broken pipe 管道异常

    return 1;
    //SIGSEGV: segmentation fault
    /*char* a = (char *)0xfff;
    char t = a[0] - '0';*/

   //SIGABRT: out of memory
/*    unsigned long size = 3 * Giga;
    char *b =  new char[size];
    memset(b, 11, size);
    if (!b) {
        LOGE("fvsystem", "out of memory!!!");
    }else{
        LOGD("fvsystem", "%p, last value = %d", b, (int)b[size - 1]);
    }


    //SIGSEGV: stack overflow
    char c[256*Mega] = {0};*/
}
import android.util.Log;

public class JniInterface {
    static {
        System.loadLibrary("exception_handler");
    }

    public static int testNativeException(int param){

        return jniTestException(param);
    }

    public static int initExceptionHandler(){
        return jniInitExceptionHandler();
    }

    public static native int jniInitExceptionHandler();


    public static native int jniTestException(int param);
    
   public static void notifyNativeException(String exceptionSummary){
        Log.e("JniInterfacexx", "java msg" + exceptionSummary);
        APP.getInstance().handleException(new Exception(exceptionSummary));
    }
}

参考文章:
android和iOS平台的崩溃捕获和收集
JNI Crash:异常定位与捕获处理
CrashHandler.h
setjmp和longjmp函数使用详解
linux系统编程之信号(七):被信号中断的系统调用和库函数处理方式
what is the use of SA_ONSTACK in sigaction
signals/t_sigaltstack.c
sigsetjmp() — Save stack environment and signal mask
Linux——信号掩码(signal mask)

你可能感兴趣的:(JNI 异常捕获)