Android JNI开发(二)

引言

在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 
#include 

#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(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(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 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(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(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 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:异常定位与捕获处理

欢迎交流、点赞、转载,码字不易,转载请注明出处。

你可能感兴趣的:(Android JNI开发(二))