JNI 異常捕獲

update:

以下的代碼不是很好用, 只是demo性質(zhì).
我目前用的是現(xiàn)成的框架: 愛奇藝的xCrash
可以setNativeCallback
有興趣的可以研究此項(xiàng)目的代碼

deprecated:

實(shí)例代碼:
(待優(yōu)化點(diǎn)是把GetStaticMethodID等反射得到的結(jié)果緩存起來, 因?yàn)榉瓷涞姆椒ū容^耗資源,
所以在Crash時(shí)直接用緩存的數(shù)據(jù), 能增加代碼的健壯性)

#include <jni.h>
#include <string>
#include "Log.h"
#include <signal.h>
#include <setjmp.h>
#include <asm/siginfo.h>
#define TAG "faceVeriSys"
#define Method(x) Java_com_xxx_jni_JniInterface_##x
using namespace std;

JavaVM* g_jvm;
// 定義代碼跳轉(zhuǎn)錨點(diǎn)
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){
        //如果已經(jīng)執(zhí)行過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"); //第一次執(zhí)行sigsetjmp,返回0
    } else {
        LOGE(TAG,"sigsetjmp from jni error"); //第一次執(zhí)行sigsetjmp,返回0
        // 從siglongjmp跳轉(zhuǎn)sigsetjmp所在行,sigsetjmp返回1,因此進(jìn)入此行
        callJavaNotifyNativeException();
        return -1;
    }
    struct sigaction sigact; // 注冊要捕捉的系統(tǒng)信號(hào)量
    //sigset_t block_mask;
    //sigemptyset(&block_mask);
    //sigaddset(&block_mask, SIGABRT); // handler處理捕捉到的信號(hào)量時(shí),需要阻塞的信號(hào)
    //sigaddset(&block_mask, SIGSEGV); // handler處理捕捉到的信號(hào)量時(shí),需要阻塞的信號(hào)
    // 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 非法內(nèi)存訪問
    sigaction(SIGILL, &sigact, NULL); // Illegal instruction 非法指令
    sigaction(SIGBUS, &sigact, NULL); // BUS error 硬件訪問異常
    sigaction(SIGFPE, &sigact, NULL); // Floating-point exception 錯(cuò)誤的算術(shù)運(yùn)算,如除以零,并非一定要涉及浮點(diǎn)算術(shù)
    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平臺(tái)的崩潰捕獲和收集
JNI Crash:異常定位與捕獲處理
CrashHandler.h
setjmp和longjmp函數(shù)使用詳解
linux系統(tǒng)編程之信號(hào)(七):被信號(hào)中斷的系統(tǒng)調(diào)用和庫函數(shù)處理方式
what is the use of SA_ONSTACK in sigaction
signals/t_sigaltstack.c
sigsetjmp() — Save stack environment and signal mask
Linux——信號(hào)掩碼(signal mask)

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

友情鏈接更多精彩內(nèi)容