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)