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

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

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,445评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,889评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,047评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,760评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,745评论 5 367
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,638评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,011评论 3 398
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,669评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,923评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,655评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,740评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,406评论 4 320
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,995评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,961评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,197评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,023评论 2 350
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,483评论 2 342