引言
在Android JNI 开发 (一) 一文中介绍了JNI的数据结构和接口函数等基础知识。但是开发过程中只有JNI基础知识是不够的,为了避免遇到问题手足无措,我们还需要具备一定的调试和解决问题的能力,本文就简单介绍JNI的调试工具和调试方法。
1. Log
Log是最基础、最常用的调试工具。即使在其它工具都不能奏效的情况下,Log却往往还能提供一些蛛丝马迹帮助我们分析和解决问题。JNI 中使用 Log很简单,只要导入 android/log.h,然后就可以调用其中的__android_log_print等接口函数来输出日志到logcat,效果与java中的android.util.Log的输出效果几乎完全一致。与java中的android.util.Log一样,也可以为日志定义TAG。例如:
#include <jni.h>
#include <android/log.h>
#define LOG_TAG "MyApp"
#define LOGD(format, ...) __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, format, ##__VA_ARGS__)
#define LOGE(format, ...) __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, format, ##__VA_ARGS__)
#define LOGI(format, ...) __android_log_print(ANDROID_LOG_INFO, LOG_TAG, format, ##__VA_ARGS__)
#define LOGW(format, ...) __android_log_print(ANDROID_LOG_WARN, LOG_TAG, format, ##__VA_ARGS__)
extern "C"
JNIEXPORT void JNICALL
Java_com_qxt_myapp_MainActivity_testObject(JNIEnv *env, jobject thiz) {
jclass clazz = env->FindClass("com/qxt/myapp/MainActivity");
if (clazz != nullptr) {
//Access object field
jfieldID ageID = env->GetFieldID(clazz, "age", "I");
jint ageInt = (jint) env->GetIntField(thiz, ageID);
//Access object method
jmethodID getAgeID = env->GetMethodID(clazz, "getAge", "(Ljava/lang/String;)Ljava/lang/String;");
jstring nameStr = env->NewStringUTF("JNI");
jstring msgStr = (jstring) env->CallObjectMethod(thiz, getAgeID, nameStr);
//Use string, convert jstring to char sequence
char *name = (char *) env->GetStringUTFChars(nameStr, NULL);
char *msg = (char *) env->GetStringUTFChars(msgStr, NULL);
LOGD("[testObject] message:%s; age:%d", msg, ageInt);
env->ReleaseStringUTFChars(nameStr, name);
env->ReleaseStringUTFChars(msgStr, msg);
}
env->DeleteLocalRef(clazz);
}
日志输出:
2020-08-14 14:17:47.811 17164-17164/com.qxt.myapp D/MyApp: [testObject] message:Hello JNI, I'm java method getAge; age:20
2. 异常处理
同java开发一样,异常处理也是JNI开发中很重要的工作,只有妥善了处理了异常,程序才能顺利执行。JNI代码执行时如果遇到异常,程序并不会停止执行,而是会继续执行下一句代码,直到崩溃发生。Java中捕获和处理异常有try-catch机制,JNI也提供了类似的机制。它就是在Android JNI 开发 (一) 的第9节中我们提到过的ExceptionCheck。JNI提供了ExceptionCheck、ExceptionDescribe等接口,它们可以捕获异常,并将异常的堆栈信息输出到logcat。例如:
MainActivity:
public String getName(String name) {
throw new IllegalArgumentException("test");
}
public native void testException();
native-lib.cpp:
extern "C"
JNIEXPORT void JNICALL
Java_com_qxt_myapp_MainActivity_testException(JNIEnv *env, jobject thiz) {
jclass clazz = env->FindClass("com/qxt/myapp/MainActivity");
if (clazz != nullptr) {
//Access object method
jmethodID getName = env->GetMethodID(clazz, "getName", "(Ljava/lang/String;)Ljava/lang/String;");
if (getName != nullptr) {
jstring nameStr = env->NewStringUTF("JNI");
jstring msgStr = (jstring) env->CallObjectMethod(thiz, getName, nameStr);
if (env->ExceptionCheck()) {
LOGD("[testException] found an exception");
env->ExceptionDescribe();
env->ExceptionClear();
}
}
}
env->DeleteLocalRef(clazz);
}
在本地函数testException中我们调用了普通的Java方法getName,getName中会抛出一个异常IllegalArgumentException。运行一下这段代码,我们可以在logcat中发现异常的堆栈信息:
2020-08-14 15:02:20.790 18893-18893/com.qxt.myapp D/MyApp: [testException] found an exception
2020-08-14 15:02:20.791 18893-18893/com.qxt.myapp W/System.err: java.lang.IllegalArgumentException: test
2020-08-14 15:02:20.791 18893-18893/com.qxt.myapp W/System.err: at com.qxt.myapp.MainActivity.getName(MainActivity.java:61)
2020-08-14 15:02:20.791 18893-18893/com.qxt.myapp W/System.err: at com.qxt.myapp.MainActivity.testException(Native Method)
2020-08-14 15:02:20.791 18893-18893/com.qxt.myapp W/System.err: at com.qxt.myapp.MainActivity.onCreate(MainActivity.java:39)
2020-08-14 15:02:20.791 18893-18893/com.qxt.myapp W/System.err: at android.app.Activity.performCreate(Activity.java:7136)
2020-08-14 15:02:20.791 18893-18893/com.qxt.myapp W/System.err: at android.app.Activity.performCreate(Activity.java:7127)
2020-08-14 15:02:20.791 18893-18893/com.qxt.myapp W/System.err: at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1271)
2020-08-14 15:02:20.792 18893-18893/com.qxt.myapp W/System.err: at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2924)
2020-08-14 15:02:20.792 18893-18893/com.qxt.myapp W/System.err: at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:3079)
2020-08-14 15:02:20.792 18893-18893/com.qxt.myapp W/System.err: at android.app.servertransaction.LaunchActivityItem.execute(LaunchActivityItem.java:78)
2020-08-14 15:02:20.792 18893-18893/com.qxt.myapp W/System.err: at android.app.servertransaction.TransactionExecutor.executeCallbacks(TransactionExecutor.java:108)
2020-08-14 15:02:20.792 18893-18893/com.qxt.myapp W/System.err: at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:68)
2020-08-14 15:02:20.792 18893-18893/com.qxt.myapp W/System.err: at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1836)
2020-08-14 15:02:20.792 18893-18893/com.qxt.myapp W/System.err: at android.os.Handler.dispatchMessage(Handler.java:106)
2020-08-14 15:02:20.792 18893-18893/com.qxt.myapp W/System.err: at android.os.Looper.loop(Looper.java:193)
2020-08-14 15:02:20.792 18893-18893/com.qxt.myapp W/System.err: at android.app.ActivityThread.main(ActivityThread.java:6702)
2020-08-14 15:02:20.792 18893-18893/com.qxt.myapp W/System.err: at java.lang.reflect.Method.invoke(Native Method)
2020-08-14 15:02:20.792 18893-18893/com.qxt.myapp W/System.err: at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:493)
2020-08-14 15:02:20.792 18893-18893/com.qxt.myapp W/System.err: at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:911)
假如我们把ExceptionCheck这个判断的代码注释掉,不捕获这个异常,再次运行这段代码,就会出现崩溃:
2020-08-14 14:58:05.597 16085-16085/com.qxt.myapp E/AndroidRuntime: FATAL EXCEPTION: main
Process: com.qxt.myapp, PID: 16085
java.lang.RuntimeException: Unable to start activity ComponentInfo{com.qxt.myapp/com.qxt.myapp.MainActivity}: java.lang.IllegalArgumentException: test
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2944)
at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:3079)
at android.app.servertransaction.LaunchActivityItem.execute(LaunchActivityItem.java:78)
at android.app.servertransaction.TransactionExecutor.executeCallbacks(TransactionExecutor.java:108)
at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:68)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1836)
at android.os.Handler.dispatchMessage(Handler.java:106)
at android.os.Looper.loop(Looper.java:193)
at android.app.ActivityThread.main(ActivityThread.java:6702)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:493)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:911)
Caused by: java.lang.IllegalArgumentException: test
at com.qxt.myapp.MainActivity.getName(MainActivity.java:61)
at com.qxt.myapp.MainActivity.testException(Native Method)
at com.qxt.myapp.MainActivity.onCreate(MainActivity.java:39)
at android.app.Activity.performCreate(Activity.java:7136)
at android.app.Activity.performCreate(Activity.java:7127)
at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1271)
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2924)
at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:3079)
at android.app.servertransaction.LaunchActivityItem.execute(LaunchActivityItem.java:78)
at android.app.servertransaction.TransactionExecutor.executeCallbacks(TransactionExecutor.java:108)
at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:68)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1836)
at android.os.Handler.dispatchMessage(Handler.java:106)
at android.os.Looper.loop(Looper.java:193)
at android.app.ActivityThread.main(ActivityThread.java:6702)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:493)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:911)
除了最常用的ExceptionCheck、ExceptionDescribe、ExceptionClear等接口外,JNI还有ExceptionOccurred接口,它也可以用来确定是否发生异常,以便我们妥善处理遇到的异常。
3. 断点
现在使用Android Studio开发JNI是非常方便的,同java代码一样,Android Studio也支持在JNI的C/C++中打断点进行调试。调试的步骤也基本是一样的:
- 在Android Studio代码编辑器的左侧(行号之后的空白处)点击鼠标打上断点。
- 点击小虫图标(Debug app)或者快捷键Shift +F9进行调试。
- 等待调试运行起来后,可以打开底部的Debug面板进行操作,可以跳到下一行,跳进某个函数、跳到下个断点、停止调试等等。
想了解更多调试相关的知识请参考官网:https://developer.android.google.cn/studio/debug
4. ndk-stack
通常情况下,native的崩溃和异常比java更不直观,比较难排查,但是我们可以借助NDK中的ndk-stack和addr2line两个工具来还原堆栈信息,找到出问题的代码所在的文件及行号。本节我们先来介绍ndk-stack,下节介绍addr2line。
首先,我们先来故意制造一个崩溃:
extern "C"
JNIEXPORT void JNICALL
Java_com_qxt_myapp_MainActivity_testCrash(JNIEnv *env, jobject thiz) {
int* i = NULL;
i[0] = 0;//实际CPP源文件158行
}
运行这段代码后出现崩溃,我们抓到的日志是这样的:
2020-08-14 03:36:30.656 8866-8866/? I/crash_dump64: obtaining output fd from tombstoned, type: kDebuggerdTombstone
2020-08-14 03:36:30.672 8866-8866/? A/DEBUG: *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
2020-08-14 03:36:30.672 8866-8866/? A/DEBUG: Native Crash TIME: 99296997
2020-08-14 03:36:30.672 8866-8866/? A/DEBUG: *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
2020-08-14 03:36:30.672 8866-8866/? A/DEBUG: Build fingerprint: 'Symphony/i98/i98:10/QP1A.190711.020/55:user/release-keys'
2020-08-14 03:36:30.672 8866-8866/? A/DEBUG: Revision: '0'
2020-08-14 03:36:30.672 8866-8866/? A/DEBUG: ABI: 'arm64'
2020-08-14 03:36:30.673 8866-8866/? A/DEBUG: Timestamp: 2020-08-14 01:36:30+0600
2020-08-14 03:36:30.673 8866-8866/? A/DEBUG: pid: 8819, tid: 8819, name: com.qxt.myapp >>> com.qxt.myapp <<<
2020-08-14 03:36:30.673 8866-8866/? A/DEBUG: uid: 10158
2020-08-14 03:36:30.673 8866-8866/? A/DEBUG: signal 11 (SIGSEGV), code 1 (SEGV_MAPERR), fault addr 0x0
2020-08-14 03:36:30.673 8866-8866/? A/DEBUG: Cause: null pointer dereference
2020-08-14 03:36:30.673 8866-8866/? A/DEBUG: x0 0000007e55dd66c0 x1 0000007fc96ef4a4 x2 0000007fc96f0690 x3 0000007dd0b0a4f0
2020-08-14 03:36:30.673 8866-8866/? A/DEBUG: x4 0000007fc96ef5a0 x5 0000007dd0dbf874 x6 0000007fc96ef600 x7 0000000070400480
2020-08-14 03:36:30.673 8866-8866/? A/DEBUG: x8 0000000000000000 x9 f4d9345c68992d53 x10 0000000000430000 x11 0000007dc0000000
2020-08-14 03:36:30.673 8866-8866/? A/DEBUG: x12 0000000000000030 x13 0000000004a1c818 x14 0000000000000006 x15 ffffffffffffffff
2020-08-14 03:36:30.674 8866-8866/? A/DEBUG: x16 0000007dc05557c8 x17 0000007e530d38c0 x18 0000007e56690000 x19 0000007e55d19c00
2020-08-14 03:36:30.674 8866-8866/? A/DEBUG: x20 0000000000000000 x21 0000007e55d19c00 x22 0000007fc96ef710 x23 0000007dc82970ba
2020-08-14 03:36:30.674 8866-8866/? A/DEBUG: x24 0000000000000004 x25 0000007e55ed4020 x26 0000007e55d19cb0 x27 0000000000000001
2020-08-14 03:36:30.674 8866-8866/? A/DEBUG: x28 0000007fc96ef4a0 x29 0000007fc96ef4a0
2020-08-14 03:36:30.674 8866-8866/? A/DEBUG: sp 0000007fc96ef460 lr 0000007dd0965354 pc 0000007dc05557e0
2020-08-14 03:36:31.437 8866-8866/? A/DEBUG: backtrace:
2020-08-14 03:36:31.437 8866-8866/? A/DEBUG: #00 pc 00000000000107e0 /data/app/com.qxt.myapp-mpm2LPzx1fA48I99gUSq-A==/base.apk!libnative-lib.so (offset 0x193000) (Java_com_qxt_myapp_MainActivity_testCrash+24) (BuildId: 9d5b5e236c849a878072b6e11ce4d15b1860df0f)
2020-08-14 03:36:31.438 8866-8866/? A/DEBUG: #01 pc 000000000013f350 /apex/com.android.runtime/lib64/libart.so (art_quick_generic_jni_trampoline+144) (BuildId: 3202c08af4daa021ee265eb0c1cc0a00)
2020-08-14 03:36:31.438 8866-8866/? A/DEBUG: #02 pc 0000000000136334 /apex/com.android.runtime/lib64/libart.so (art_quick_invoke_stub+548) (BuildId: 3202c08af4daa021ee265eb0c1cc0a00)
2020-08-14 03:36:31.438 8866-8866/? A/DEBUG: #03 pc 000000000014506c /apex/com.android.runtime/lib64/libart.so (art::ArtMethod::Invoke(art::Thread*, unsigned int*, unsigned int, art::JValue*, char const*)+244) (BuildId: 3202c08af4daa021ee265eb0c1cc0a00)
2020-08-14 03:36:31.438 8866-8866/? A/DEBUG: #04 pc 00000000002df720 /apex/com.android.runtime/lib64/libart.so (art::interpreter::ArtInterpreterToCompiledCodeBridge(art::Thread*, art::ArtMethod*, art::ShadowFrame*, unsigned short, art::JValue*)+384) (BuildId: 3202c08af4daa021ee265eb0c1cc0a00)
2020-08-14 03:36:31.438 8866-8866/? A/DEBUG: #05 pc 00000000002daa00 /apex/com.android.runtime/lib64/libart.so (bool art::interpreter::DoCall<false, false>(art::ArtMethod*, art::Thread*, art::ShadowFrame&, art::Instruction const*, unsigned short, art::JValue*)+912) (BuildId: 3202c08af4daa021ee265eb0c1cc0a00)
2020-08-14 03:36:31.438 8866-8866/? A/DEBUG: #06 pc 0000000000597d6c /apex/com.android.runtime/lib64/libart.so (MterpInvokeVirtual+648) (BuildId: 3202c08af4daa021ee265eb0c1cc0a00)
2020-08-14 03:36:31.438 8866-8866/? A/DEBUG: #07 pc 0000000000130814 /apex/com.android.runtime/lib64/libart.so (mterp_op_invoke_virtual+20) (BuildId: 3202c08af4daa021ee265eb0c1cc0a00)
2020-08-14 03:36:31.438 8866-8866/? A/DEBUG: #08 pc 0000000000010c34 [anon:dalvik-classes2.dex extracted in memory from /data/app/com.qxt.myapp-mpm2LPzx1fA48I99gUSq-A==/base.apk!classes2.dex] (com.qxt.myapp.MainActivity$1.onClick+4)
2020-08-14 03:36:31.438 8866-8866/? A/DEBUG: #09 pc 0000000000599870 /apex/com.android.runtime/lib64/libart.so (MterpInvokeInterface+1740) (BuildId: 3202c08af4daa021ee265eb0c1cc0a00)
2020-08-14 03:36:31.438 8866-8866/? A/DEBUG: #10 pc 0000000000130a14 /apex/com.android.runtime/lib64/libart.so (mterp_op_invoke_interface+20) (BuildId: 3202c08af4daa021ee265eb0c1cc0a00)
2020-08-14 03:36:31.439 8866-8866/? A/DEBUG: #11 pc 00000000001a7a0e /system/framework/framework.jar (android.view.View.performClick+34)
2020-08-14 03:36:31.439 8866-8866/? A/DEBUG: #12 pc 000000000059807c /apex/com.android.runtime/lib64/libart.so (MterpInvokeVirtual+1432) (BuildId: 3202c08af4daa021ee265eb0c1cc0a00)
2020-08-14 03:36:31.439 8866-8866/? A/DEBUG: #13 pc 0000000000130814 /apex/com.android.runtime/lib64/libart.so (mterp_op_invoke_virtual+20) (BuildId: 3202c08af4daa021ee265eb0c1cc0a00)
2020-08-14 03:36:31.439 8866-8866/? A/DEBUG: #14 pc 00000000001a7a86 /system/framework/framework.jar (android.view.View.performClickInternal+6)
2020-08-14 03:36:31.439 8866-8866/? A/DEBUG: #15 pc 000000000059a40c /apex/com.android.runtime/lib64/libart.so (MterpInvokeDirect+1168) (BuildId: 3202c08af4daa021ee265eb0c1cc0a00)
2020-08-14 03:36:31.439 8866-8866/? A/DEBUG: #16 pc 0000000000130914 /apex/com.android.runtime/lib64/libart.so (mterp_op_invoke_direct+20) (BuildId: 3202c08af4daa021ee265eb0c1cc0a00)
2020-08-14 03:36:31.439 8866-8866/? A/DEBUG: #17 pc 00000000001a3304 /system/framework/framework.jar (android.view.View.access$3500)
2020-08-14 03:36:31.439 8866-8866/? A/DEBUG: #18 pc 000000000059ac18 /apex/com.android.runtime/lib64/libart.so (MterpInvokeStatic+1136) (BuildId: 3202c08af4daa021ee265eb0c1cc0a00)
2020-08-14 03:36:31.439 8866-8866/? A/DEBUG: #19 pc 0000000000130994 /apex/com.android.runtime/lib64/libart.so (mterp_op_invoke_static+20) (BuildId: 3202c08af4daa021ee265eb0c1cc0a00)
2020-08-14 03:36:31.439 8866-8866/? A/DEBUG: #20 pc 0000000000182b28 /system/framework/framework.jar (android.view.View$PerformClick.run+16)
2020-08-14 03:36:31.439 8866-8866/? A/DEBUG: #21 pc 0000000000599870 /apex/com.android.runtime/lib64/libart.so (MterpInvokeInterface+1740) (BuildId: 3202c08af4daa021ee265eb0c1cc0a00)
2020-08-14 03:36:31.439 8866-8866/? A/DEBUG: #22 pc 0000000000130a14 /apex/com.android.runtime/lib64/libart.so (mterp_op_invoke_interface+20) (BuildId: 3202c08af4daa021ee265eb0c1cc0a00)
2020-08-14 03:36:31.439 8866-8866/? A/DEBUG: #23 pc 000000000030435c /system/framework/framework.jar (android.os.Handler.handleCallback+4)
2020-08-14 03:36:31.439 8866-8866/? A/DEBUG: #24 pc 000000000059ac18 /apex/com.android.runtime/lib64/libart.so (MterpInvokeStatic+1136) (BuildId: 3202c08af4daa021ee265eb0c1cc0a00)
2020-08-14 03:36:31.440 8866-8866/? A/DEBUG: #25 pc 0000000000130994 /apex/com.android.runtime/lib64/libart.so (mterp_op_invoke_static+20) (BuildId: 3202c08af4daa021ee265eb0c1cc0a00)
2020-08-14 03:36:31.440 8866-8866/? A/DEBUG: #26 pc 00000000003041c8 /system/framework/framework.jar (android.os.Handler.dispatchMessage+8)
2020-08-14 03:36:31.440 8866-8866/? A/DEBUG: #27 pc 000000000059807c /apex/com.android.runtime/lib64/libart.so (MterpInvokeVirtual+1432) (BuildId: 3202c08af4daa021ee265eb0c1cc0a00)
2020-08-14 03:36:31.440 8866-8866/? A/DEBUG: #28 pc 0000000000130814 /apex/com.android.runtime/lib64/libart.so (mterp_op_invoke_virtual+20) (BuildId: 3202c08af4daa021ee265eb0c1cc0a00)
2020-08-14 03:36:31.440 8866-8866/? A/DEBUG: #29 pc 000000000032a672 /system/framework/framework.jar (android.os.Looper.loop+466)
2020-08-14 03:36:31.440 8866-8866/? A/DEBUG: #30 pc 000000000059ac18 /apex/com.android.runtime/lib64/libart.so (MterpInvokeStatic+1136) (BuildId: 3202c08af4daa021ee265eb0c1cc0a00)
2020-08-14 03:36:31.440 8866-8866/? A/DEBUG: #31 pc 0000000000130994 /apex/com.android.runtime/lib64/libart.so (mterp_op_invoke_static+20) (BuildId: 3202c08af4daa021ee265eb0c1cc0a00)
2020-08-14 03:36:31.440 8866-8866/? A/DEBUG: #32 pc 0000000000193346 /system/framework/framework.jar (android.app.ActivityThread.main+430)
2020-08-14 03:36:31.440 8866-8866/? A/DEBUG: #33 pc 00000000002b036c /apex/com.android.runtime/lib64/libart.so (_ZN3art11interpreterL7ExecuteEPNS_6ThreadERKNS_20CodeItemDataAccessorERNS_11ShadowFrameENS_6JValueEbb.llvm.6052660250618262909+240) (BuildId: 3202c08af4daa021ee265eb0c1cc0a00)
2020-08-14 03:36:31.440 8866-8866/? A/DEBUG: #34 pc 0000000000589374 /apex/com.android.runtime/lib64/libart.so (artQuickToInterpreterBridge+1012) (BuildId: 3202c08af4daa021ee265eb0c1cc0a00)
2020-08-14 03:36:31.440 8866-8866/? A/DEBUG: #35 pc 000000000013f468 /apex/com.android.runtime/lib64/libart.so (art_quick_to_interpreter_bridge+88) (BuildId: 3202c08af4daa021ee265eb0c1cc0a00)
2020-08-14 03:36:31.440 8866-8866/? A/DEBUG: #36 pc 00000000001365b8 /apex/com.android.runtime/lib64/libart.so (art_quick_invoke_static_stub+568) (BuildId: 3202c08af4daa021ee265eb0c1cc0a00)
2020-08-14 03:36:31.440 8866-8866/? A/DEBUG: #37 pc 000000000014508c /apex/com.android.runtime/lib64/libart.so (art::ArtMethod::Invoke(art::Thread*, unsigned int*, unsigned int, art::JValue*, char const*)+276) (BuildId: 3202c08af4daa021ee265eb0c1cc0a00)
2020-08-14 03:36:31.440 8866-8866/? A/DEBUG: #38 pc 00000000004a9674 /apex/com.android.runtime/lib64/libart.so (art::(anonymous namespace)::InvokeWithArgArray(art::ScopedObjectAccessAlreadyRunnable const&, art::ArtMethod*, art::(anonymous namespace)::ArgArray*, art::JValue*, char const*)+104) (BuildId: 3202c08af4daa021ee265eb0c1cc0a00)
2020-08-14 03:36:31.440 8866-8866/? A/DEBUG: #39 pc 00000000004ab09c /apex/com.android.runtime/lib64/libart.so (art::InvokeMethod(art::ScopedObjectAccessAlreadyRunnable const&, _jobject*, _jobject*, _jobject*, unsigned long)+1476) (BuildId: 3202c08af4daa021ee265eb0c1cc0a00)
2020-08-14 03:36:31.441 8866-8866/? A/DEBUG: #40 pc 0000000000437ab0 /apex/com.android.runtime/lib64/libart.so (art::Method_invoke(_JNIEnv*, _jobject*, _jobject*, _jobjectArray*)+52) (BuildId: 3202c08af4daa021ee265eb0c1cc0a00)
2020-08-14 03:36:31.441 8866-8866/? A/DEBUG: #41 pc 00000000006d3374 /system/framework/arm64/boot.oat (art_jni_trampoline+180) (BuildId: 7e6536fb26a76f1b84b0c56847c93a5238a88664)
2020-08-14 03:36:31.441 8866-8866/? A/DEBUG: #42 pc 0000000000136334 /apex/com.android.runtime/lib64/libart.so (art_quick_invoke_stub+548) (BuildId: 3202c08af4daa021ee265eb0c1cc0a00)
2020-08-14 03:36:31.441 8866-8866/? A/DEBUG: #43 pc 000000000014506c /apex/com.android.runtime/lib64/libart.so (art::ArtMethod::Invoke(art::Thread*, unsigned int*, unsigned int, art::JValue*, char const*)+244) (BuildId: 3202c08af4daa021ee265eb0c1cc0a00)
2020-08-14 03:36:31.441 8866-8866/? A/DEBUG: #44 pc 00000000002df720 /apex/com.android.runtime/lib64/libart.so (art::interpreter::ArtInterpreterToCompiledCodeBridge(art::Thread*, art::ArtMethod*, art::ShadowFrame*, unsigned short, art::JValue*)+384) (BuildId: 3202c08af4daa021ee265eb0c1cc0a00)
2020-08-14 03:36:31.441 8866-8866/? A/DEBUG: #45 pc 00000000002daa00 /apex/com.android.runtime/lib64/libart.so (bool art::interpreter::DoCall<false, false>(art::ArtMethod*, art::Thread*, art::ShadowFrame&, art::Instruction const*, unsigned short, art::JValue*)+912) (BuildId: 3202c08af4daa021ee265eb0c1cc0a00)
2020-08-14 03:36:31.441 8866-8866/? A/DEBUG: #46 pc 0000000000597d6c /apex/com.android.runtime/lib64/libart.so (MterpInvokeVirtual+648) (BuildId: 3202c08af4daa021ee265eb0c1cc0a00)
2020-08-14 03:36:31.441 8866-8866/? A/DEBUG: #47 pc 0000000000130814 /apex/com.android.runtime/lib64/libart.so (mterp_op_invoke_virtual+20) (BuildId: 3202c08af4daa021ee265eb0c1cc0a00)
2020-08-14 03:36:31.441 8866-8866/? A/DEBUG: #48 pc 0000000000356f36 /system/framework/framework.jar (com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run+22)
2020-08-14 03:36:31.441 8866-8866/? A/DEBUG: #49 pc 00000000002b036c /apex/com.android.runtime/lib64/libart.so (_ZN3art11interpreterL7ExecuteEPNS_6ThreadERKNS_20CodeItemDataAccessorERNS_11ShadowFrameENS_6JValueEbb.llvm.6052660250618262909+240) (BuildId: 3202c08af4daa021ee265eb0c1cc0a00)
2020-08-14 03:36:31.441 8866-8866/? A/DEBUG: #50 pc 0000000000589374 /apex/com.android.runtime/lib64/libart.so (artQuickToInterpreterBridge+1012) (BuildId: 3202c08af4daa021ee265eb0c1cc0a00)
2020-08-14 03:36:31.441 8866-8866/? A/DEBUG: #51 pc 000000000013f468 /apex/com.android.runtime/lib64/libart.so (art_quick_to_interpreter_bridge+88) (BuildId: 3202c08af4daa021ee265eb0c1cc0a00)
2020-08-14 03:36:31.441 8866-8866/? A/DEBUG: #52 pc 0000000002116c6c /system/framework/arm64/boot-framework.oat (com.android.internal.os.ZygoteInit.main+2076) (BuildId: aa37c86acfca1958ca3d1f1423214af04ae501f8)
2020-08-14 03:36:31.441 8866-8866/? A/DEBUG: #53 pc 00000000001365b8 /apex/com.android.runtime/lib64/libart.so (art_quick_invoke_static_stub+568) (BuildId: 3202c08af4daa021ee265eb0c1cc0a00)
2020-08-14 03:36:31.441 8866-8866/? A/DEBUG: #54 pc 000000000014508c /apex/com.android.runtime/lib64/libart.so (art::ArtMethod::Invoke(art::Thread*, unsigned int*, unsigned int, art::JValue*, char const*)+276) (BuildId: 3202c08af4daa021ee265eb0c1cc0a00)
2020-08-14 03:36:31.441 8866-8866/? A/DEBUG: #55 pc 00000000004a9674 /apex/com.android.runtime/lib64/libart.so (art::(anonymous namespace)::InvokeWithArgArray(art::ScopedObjectAccessAlreadyRunnable const&, art::ArtMethod*, art::(anonymous namespace)::ArgArray*, art::JValue*, char const*)+104) (BuildId: 3202c08af4daa021ee265eb0c1cc0a00)
2020-08-14 03:36:31.442 8866-8866/? A/DEBUG: #56 pc 00000000004a92e0 /apex/com.android.runtime/lib64/libart.so (art::InvokeWithVarArgs(art::ScopedObjectAccessAlreadyRunnable const&, _jobject*, _jmethodID*, std::__va_list)+408) (BuildId: 3202c08af4daa021ee265eb0c1cc0a00)
2020-08-14 03:36:31.442 8866-8866/? A/DEBUG: #57 pc 00000000003b67b4 /apex/com.android.runtime/lib64/libart.so (art::JNI::CallStaticVoidMethodV(_JNIEnv*, _jclass*, _jmethodID*, std::__va_list)+628) (BuildId: 3202c08af4daa021ee265eb0c1cc0a00)
2020-08-14 03:36:31.442 8866-8866/? A/DEBUG: #58 pc 00000000000bf560 /system/lib64/libandroid_runtime.so (_JNIEnv::CallStaticVoidMethod(_jclass*, _jmethodID*, ...)+116) (BuildId: 68ffb68d2f18afa0e7728e1e0e6d81c3)
2020-08-14 03:36:31.442 8866-8866/? A/DEBUG: #59 pc 00000000000c23e8 /system/lib64/libandroid_runtime.so (android::AndroidRuntime::start(char const*, android::Vector<android::String8> const&, bool)+780) (BuildId: 68ffb68d2f18afa0e7728e1e0e6d81c3)
2020-08-14 03:36:31.442 8866-8866/? A/DEBUG: #60 pc 00000000000034e0 /system/bin/app_process64 (main+1168) (BuildId: 17c28587f58ba3244a8f195cab83135f)
2020-08-14 03:36:31.442 8866-8866/? A/DEBUG: #61 pc 000000000007d798 /apex/com.android.runtime/lib64/bionic/libc.so (__libc_init+108) (BuildId: 676a709a0ee633ec9cf6ab05ec6410ae)
2020-08-14 03:36:32.103 8819-8819/com.qxt.myapp I/DEBUG: Crash thread dumpable
可以看到,日志里面有一些内存地址,还指明了出错的文件及函数:
Java_com_qxt_myapp_MainActivity_testCrash+24
但是却没有指明具体的出错的行号。虽然即使没有行号我们也可以结合源代码分析出错的位置,但是如果有具体的行号,我们就可以更加迅速的定位问题。
ndk-stack是NDK中的一个工具,它允许把文件或者logcat当做输入,用来还原错误或异常的内存地址所对应的堆栈信息,还原后可以很直观的看到出问题的代码行号。还原时ndk-stack需要指定保留符号链接的so的路径。
文件作为输入还原:
qxt@ubuntu:~/code/open-source/MyApp$ adb logcat > error.txt
qxt@ubuntu:~/code/open-source/MyApp$ ndk-stack -sym app/build/intermediates/cmake/debug/obj/arm64-v8a/ -dump error.txt
logcat当做输入还原:
qxt@ubuntu:~/code/open-source/MyApp$ adb logcat | ndk-stack -sym app/build/intermediates/cmake/debug/obj/arm64-v8a/
还原后:
********** Crash dump: **********
Build fingerprint: 'Symphony/i98/i98:10/QP1A.190711.020/55:user/release-keys'
#00 0x00000000000107e0 /data/app/com.qxt.myapp-mpm2LPzx1fA48I99gUSq-A==/base.apk!libnative-lib.so (offset 0x193000) (Java_com_qxt_myapp_MainActivity_testCrash+24) (BuildId: 9d5b5e236c849a878072b6e11ce4d15b1860df0f)
Java_com_qxt_myapp_MainActivity_testCrash
/mnt/sdb/home/qxt/code/open-source/MyApp/app/src/main/cpp/native-lib.cpp:158:10
#01 0x000000000013f350 /apex/com.android.runtime/lib64/libart.so (art_quick_generic_jni_trampoline+144) (BuildId: 3202c08af4daa021ee265eb0c1cc0a00)
#02 0x0000000000136334 /apex/com.android.runtime/lib64/libart.so (art_quick_invoke_stub+548) (BuildId: 3202c08af4daa021ee265eb0c1cc0a00)
#03 0x000000000014506c /apex/com.android.runtime/lib64/libart.so (art::ArtMethod::Invoke(art::Thread*, unsigned int*, unsigned int, art::JValue*, char const*)+244) (BuildId: 3202c08af4daa021ee265eb0c1cc0a00)
#04 0x00000000002df720 /apex/com.android.runtime/lib64/libart.so (art::interpreter::ArtInterpreterToCompiledCodeBridge(art::Thread*, art::ArtMethod*, art::ShadowFrame*, unsigned short, art::JValue*)+384) (BuildId: 3202c08af4daa021ee265eb0c1cc0a00)
#05 0x00000000002daa00 /apex/com.android.runtime/lib64/libart.so (bool art::interpreter::DoCall<false, false>(art::ArtMethod*, art::Thread*, art::ShadowFrame&, art::Instruction const*, unsigned short, art::JValue*)+912) (BuildId: 3202c08af4daa021ee265eb0c1cc0a00)
#06 0x0000000000597d6c /apex/com.android.runtime/lib64/libart.so (MterpInvokeVirtual+648) (BuildId: 3202c08af4daa021ee265eb0c1cc0a00)
#07 0x0000000000130814 /apex/com.android.runtime/lib64/libart.so (mterp_op_invoke_virtual+20) (BuildId: 3202c08af4daa021ee265eb0c1cc0a00)
#08 0x0000000000010c34 [anon:dalvik-classes2.dex extracted in memory from /data/app/com.qxt.myapp-mpm2LPzx1fA48I99gUSq-A==/base.apk!classes2.dex] (com.qxt.myapp.MainActivity$1.onClick+4)
#09 0x0000000000599870 /apex/com.android.runtime/lib64/libart.so (MterpInvokeInterface+1740) (BuildId: 3202c08af4daa021ee265eb0c1cc0a00)
#10 0x0000000000130a14 /apex/com.android.runtime/lib64/libart.so (mterp_op_invoke_interface+20) (BuildId: 3202c08af4daa021ee265eb0c1cc0a00)
#11 0x00000000001a7a0e /system/framework/framework.jar (android.view.View.performClick+34)
#12 0x000000000059807c /apex/com.android.runtime/lib64/libart.so (MterpInvokeVirtual+1432) (BuildId: 3202c08af4daa021ee265eb0c1cc0a00)
#13 0x0000000000130814 /apex/com.android.runtime/lib64/libart.so (mterp_op_invoke_virtual+20) (BuildId: 3202c08af4daa021ee265eb0c1cc0a00)
#14 0x00000000001a7a86 /system/framework/framework.jar (android.view.View.performClickInternal+6)
#15 0x000000000059a40c /apex/com.android.runtime/lib64/libart.so (MterpInvokeDirect+1168) (BuildId: 3202c08af4daa021ee265eb0c1cc0a00)
#16 0x0000000000130914 /apex/com.android.runtime/lib64/libart.so (mterp_op_invoke_direct+20) (BuildId: 3202c08af4daa021ee265eb0c1cc0a00)
#17 0x00000000001a3304 /system/framework/framework.jar (android.view.View.access$3500)
#18 0x000000000059ac18 /apex/com.android.runtime/lib64/libart.so (MterpInvokeStatic+1136) (BuildId: 3202c08af4daa021ee265eb0c1cc0a00)
#19 0x0000000000130994 /apex/com.android.runtime/lib64/libart.so (mterp_op_invoke_static+20) (BuildId: 3202c08af4daa021ee265eb0c1cc0a00)
#20 0x0000000000182b28 /system/framework/framework.jar (android.view.View$PerformClick.run+16)
#21 0x0000000000599870 /apex/com.android.runtime/lib64/libart.so (MterpInvokeInterface+1740) (BuildId: 3202c08af4daa021ee265eb0c1cc0a00)
#22 0x0000000000130a14 /apex/com.android.runtime/lib64/libart.so (mterp_op_invoke_interface+20) (BuildId: 3202c08af4daa021ee265eb0c1cc0a00)
#23 0x000000000030435c /system/framework/framework.jar (android.os.Handler.handleCallback+4)
#24 0x000000000059ac18 /apex/com.android.runtime/lib64/libart.so (MterpInvokeStatic+1136) (BuildId: 3202c08af4daa021ee265eb0c1cc0a00)
#25 0x0000000000130994 /apex/com.android.runtime/lib64/libart.so (mterp_op_invoke_static+20) (BuildId: 3202c08af4daa021ee265eb0c1cc0a00)
#26 0x00000000003041c8 /system/framework/framework.jar (android.os.Handler.dispatchMessage+8)
#27 0x000000000059807c /apex/com.android.runtime/lib64/libart.so (MterpInvokeVirtual+1432) (BuildId: 3202c08af4daa021ee265eb0c1cc0a00)
#28 0x0000000000130814 /apex/com.android.runtime/lib64/libart.so (mterp_op_invoke_virtual+20) (BuildId: 3202c08af4daa021ee265eb0c1cc0a00)
#29 0x000000000032a672 /system/framework/framework.jar (android.os.Looper.loop+466)
#30 0x000000000059ac18 /apex/com.android.runtime/lib64/libart.so (MterpInvokeStatic+1136) (BuildId: 3202c08af4daa021ee265eb0c1cc0a00)
#31 0x0000000000130994 /apex/com.android.runtime/lib64/libart.so (mterp_op_invoke_static+20) (BuildId: 3202c08af4daa021ee265eb0c1cc0a00)
#32 0x0000000000193346 /system/framework/framework.jar (android.app.ActivityThread.main+430)
#33 0x00000000002b036c /apex/com.android.runtime/lib64/libart.so (_ZN3art11interpreterL7ExecuteEPNS_6ThreadERKNS_20CodeItemDataAccessorERNS_11ShadowFrameENS_6JValueEbb.llvm.6052660250618262909+240) (BuildId: 3202c08af4daa021ee265eb0c1cc0a00)
#34 0x0000000000589374 /apex/com.android.runtime/lib64/libart.so (artQuickToInterpreterBridge+1012) (BuildId: 3202c08af4daa021ee265eb0c1cc0a00)
#35 0x000000000013f468 /apex/com.android.runtime/lib64/libart.so (art_quick_to_interpreter_bridge+88) (BuildId: 3202c08af4daa021ee265eb0c1cc0a00)
#36 0x00000000001365b8 /apex/com.android.runtime/lib64/libart.so (art_quick_invoke_static_stub+568) (BuildId: 3202c08af4daa021ee265eb0c1cc0a00)
#37 0x000000000014508c /apex/com.android.runtime/lib64/libart.so (art::ArtMethod::Invoke(art::Thread*, unsigned int*, unsigned int, art::JValue*, char const*)+276) (BuildId: 3202c08af4daa021ee265eb0c1cc0a00)
#38 0x00000000004a9674 /apex/com.android.runtime/lib64/libart.so (art::(anonymous namespace)::InvokeWithArgArray(art::ScopedObjectAccessAlreadyRunnable const&, art::ArtMethod*, art::(anonymous namespace)::ArgArray*, art::JValue*, char const*)+104) (BuildId: 3202c08af4daa021ee265eb0c1cc0a00)
#39 0x00000000004ab09c /apex/com.android.runtime/lib64/libart.so (art::InvokeMethod(art::ScopedObjectAccessAlreadyRunnable const&, _jobject*, _jobject*, _jobject*, unsigned long)+1476) (BuildId: 3202c08af4daa021ee265eb0c1cc0a00)
#40 0x0000000000437ab0 /apex/com.android.runtime/lib64/libart.so (art::Method_invoke(_JNIEnv*, _jobject*, _jobject*, _jobjectArray*)+52) (BuildId: 3202c08af4daa021ee265eb0c1cc0a00)
#41 0x00000000006d3374 /system/framework/arm64/boot.oat (art_jni_trampoline+180) (BuildId: 7e6536fb26a76f1b84b0c56847c93a5238a88664)
#42 0x0000000000136334 /apex/com.android.runtime/lib64/libart.so (art_quick_invoke_stub+548) (BuildId: 3202c08af4daa021ee265eb0c1cc0a00)
#43 0x000000000014506c /apex/com.android.runtime/lib64/libart.so (art::ArtMethod::Invoke(art::Thread*, unsigned int*, unsigned int, art::JValue*, char const*)+244) (BuildId: 3202c08af4daa021ee265eb0c1cc0a00)
#44 0x00000000002df720 /apex/com.android.runtime/lib64/libart.so (art::interpreter::ArtInterpreterToCompiledCodeBridge(art::Thread*, art::ArtMethod*, art::ShadowFrame*, unsigned short, art::JValue*)+384) (BuildId: 3202c08af4daa021ee265eb0c1cc0a00)
#45 0x00000000002daa00 /apex/com.android.runtime/lib64/libart.so (bool art::interpreter::DoCall<false, false>(art::ArtMethod*, art::Thread*, art::ShadowFrame&, art::Instruction const*, unsigned short, art::JValue*)+912) (BuildId: 3202c08af4daa021ee265eb0c1cc0a00)
#46 0x0000000000597d6c /apex/com.android.runtime/lib64/libart.so (MterpInvokeVirtual+648) (BuildId: 3202c08af4daa021ee265eb0c1cc0a00)
#47 0x0000000000130814 /apex/com.android.runtime/lib64/libart.so (mterp_op_invoke_virtual+20) (BuildId: 3202c08af4daa021ee265eb0c1cc0a00)
#48 0x0000000000356f36 /system/framework/framework.jar (com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run+22)
#49 0x00000000002b036c /apex/com.android.runtime/lib64/libart.so (_ZN3art11interpreterL7ExecuteEPNS_6ThreadERKNS_20CodeItemDataAccessorERNS_11ShadowFrameENS_6JValueEbb.llvm.6052660250618262909+240) (BuildId: 3202c08af4daa021ee265eb0c1cc0a00)
#50 0x0000000000589374 /apex/com.android.runtime/lib64/libart.so (artQuickToInterpreterBridge+1012) (BuildId: 3202c08af4daa021ee265eb0c1cc0a00)
#51 0x000000000013f468 /apex/com.android.runtime/lib64/libart.so (art_quick_to_interpreter_bridge+88) (BuildId: 3202c08af4daa021ee265eb0c1cc0a00)
#52 0x0000000002116c6c /system/framework/arm64/boot-framework.oat (com.android.internal.os.ZygoteInit.main+2076) (BuildId: aa37c86acfca1958ca3d1f1423214af04ae501f8)
#53 0x00000000001365b8 /apex/com.android.runtime/lib64/libart.so (art_quick_invoke_static_stub+568) (BuildId: 3202c08af4daa021ee265eb0c1cc0a00)
#54 0x000000000014508c /apex/com.android.runtime/lib64/libart.so (art::ArtMethod::Invoke(art::Thread*, unsigned int*, unsigned int, art::JValue*, char const*)+276) (BuildId: 3202c08af4daa021ee265eb0c1cc0a00)
#55 0x00000000004a9674 /apex/com.android.runtime/lib64/libart.so (art::(anonymous namespace)::InvokeWithArgArray(art::ScopedObjectAccessAlreadyRunnable const&, art::ArtMethod*, art::(anonymous namespace)::ArgArray*, art::JValue*, char const*)+104) (BuildId: 3202c08af4daa021ee265eb0c1cc0a00)
#56 0x00000000004a92e0 /apex/com.android.runtime/lib64/libart.so (art::InvokeWithVarArgs(art::ScopedObjectAccessAlreadyRunnable const&, _jobject*, _jmethodID*, std::__va_list)+408) (BuildId: 3202c08af4daa021ee265eb0c1cc0a00)
#57 0x00000000003b67b4 /apex/com.android.runtime/lib64/libart.so (art::JNI::CallStaticVoidMethodV(_JNIEnv*, _jclass*, _jmethodID*, std::__va_list)+628) (BuildId: 3202c08af4daa021ee265eb0c1cc0a00)
#58 0x00000000000bf560 /system/lib64/libandroid_runtime.so (_JNIEnv::CallStaticVoidMethod(_jclass*, _jmethodID*, ...)+116) (BuildId: 68ffb68d2f18afa0e7728e1e0e6d81c3)
#59 0x00000000000c23e8 /system/lib64/libandroid_runtime.so (android::AndroidRuntime::start(char const*, android::Vector<android::String8> const&, bool)+780) (BuildId: 68ffb68d2f18afa0e7728e1e0e6d81c3)
#60 0x00000000000034e0 /system/bin/app_process64 (main+1168) (BuildId: 17c28587f58ba3244a8f195cab83135f)
#61 0x000000000007d798 /apex/com.android.runtime/lib64/bionic/libc.so (__libc_init+108) (BuildId: 676a709a0ee633ec9cf6ab05ec6410ae)
Crash dump is completed
可以看到,还原后能明确看到出错的行号为158行:
/mnt/sdb/home/qxt/code/open-source/MyApp/app/src/main/cpp/native-lib.cpp:158:10
5. addr2line
addr2line也是NDK中的一个工具,它可以通过指定保留符号链接的so和出错的内存地址来还原出错代码的行号。我们从之前日志中可以找出错的内存地址:00000000000107e0。
还原指令:
qxt@ubuntu:~/dev/sdk/ndk-bundle/toolchains/aarch64-linux-android-4.9/prebuilt/linux-x86_64/bin$ ./aarch64-linux-android-addr2line -f -e ~/code/open-source/MyApp/app/build/intermediates/cmake/debug/obj/arm64-v8a/libnative-lib.so 00000000000107e0
Java_com_qxt_myapp_MainActivity_testCrash
/mnt/sdb/home/qxt/code/open-source/MyApp/app/src/main/cpp/native-lib.cpp:158
可以看到,还原后也能明确看到出错的行号为158行。
为什么是 00000000000107e0 这个地址呢?可以回到我们之前的出错日志,这个地址是对应我们加的libnative-lib.so的。当然,如果你拥有出错堆栈信息中所有的保留符号链接的so,堆栈的每一个地址对应一个保留符号链接的so,你都可以将它们还原成对应的行号。
看到这里的童鞋大概都会发现点什么,是的,为了准确还原JNI异常或错误的行号,我们每发一个版本,都需要保存保留符号链接的so或者符号表文件。这个和Java混淆后每发一个版本都需要保留mapping.txt文件用来还原堆栈是类似的。
6. 第三方工具
除了NDK提供的工具以外,我们还可以接入第三方的工具包来处理native的崩溃和异常问题,比较优秀的有爱奇艺的xCrash和腾讯的Bugly。本质上它们都是通过ndk-stack或者addr2line类似的方法来实现的还原的。利用它们进行还原so错误时也需要指定保留符号链接的so或者符号表文件,java错误则需要指定mapping.txt 文件。例如,你可以利用Bugly的插件,它会扫描保留符号链接的so生成符号表文件(zip),然后自动将符号表文件(zip)及mapping.txt文件上传。除此之外,Bugly还能记录APP的各类异常或错误信息、运营信息等等,功能非常强大。爱奇艺的xCrash和腾讯的Bugly都有详细的接入文档,这里就不再赘述了。
6.1 xCrash
爱奇艺 xCrash: https://github.com/iqiyi/xCrash
6.2 Bugly
腾讯Bugly: https://bugly.qq.com/docs/utility-tools/plugin-gradle-bugly/
7. 结语
JNI的开发系列的两篇写到这里就写完了,都是很基础的JNI知识。尽管实际开发中需求千变万化,Bug千奇百怪,但只要掌握了基本的三板斧,日常的JNI开发工作就应该没有问题了。衷心祝愿耐心看完了我这两篇文章的童鞋们都能掌握JNI开发。
8. 本文参考
https://developer.android.google.cn/studio/debug
https://developer.android.google.cn/ndk/guides/ndk-stack
JNI Crash:异常定位与捕获处理
欢迎交流、点赞、转载,码字不易,转载请注明出处。