Android Studio中三方so庫(kù)的源碼調(diào)試

一、需求背景

Android Studio 2.2+版本支持使用CMake構(gòu)建native庫(kù),同時(shí)調(diào)試器也能對(duì)自構(gòu)建的native代碼進(jìn)行源碼的調(diào)試,我們可以像調(diào)試java代碼一樣方便地調(diào)試native代碼。
我們很多的項(xiàng)目都依賴(lài)的so庫(kù),有些是我們單獨(dú)的庫(kù)工程中的,有些是三方提供的。如果想在主工程中調(diào)試這些庫(kù)的源碼,一種方法添加子工程或者CMake子模塊關(guān)聯(lián)源碼,作為自身構(gòu)建的一部分,當(dāng)然需要所有的子工程都編譯通過(guò)。
搭建多工程環(huán)境和構(gòu)建子工程本身就是一個(gè)耗時(shí)又麻煩的事情,我們希望和庫(kù)工程盡量不要耦合,而且調(diào)試時(shí)不需要再去構(gòu)建庫(kù)工程。

問(wèn)題:我們本地有so庫(kù)對(duì)應(yīng)版本的源碼,也有帶符號(hào)so文件,如何進(jìn)行源碼的調(diào)試?

二、工程示例

這里我們示例一個(gè)簡(jiǎn)單的庫(kù)工程mylib,它的c++代碼中封裝了一個(gè)求平均數(shù)的方法,然后java對(duì)外暴露了native的接口。

#include <jni.h>

extern "C"
JNIEXPORT jint JNICALL
Java_smarttime_tsia_com_mylib_MyLibNative_nativeCalAverage(JNIEnv *env, jclass type, jint a, jint b) {

    int sum = a+b;

    int avg = sum/2;

    return avg;
}

MyLibNative.java

package smarttime.tsia.com.mylib;

public class MyLibNative {

    static {
        System.loadLibrary("test-lib");
    }

    public static native int nativeCalAverage(int a, int b);
}

然后我們將它打包成一個(gè)armeabi-v7a架構(gòu)的so庫(kù)(libtest-lib.so):

我們APP主工程中,依賴(lài)這個(gè)三方庫(kù)(可以通過(guò)fileTree或maven),有一個(gè)按鈕點(diǎn)擊觸發(fā)調(diào)用nativeCalAverage:

public class MainActivity extends AppCompatActivity {
    ...
    void onClick(View v) {

        int ret = MyLibNative.nativeCalAverage(3,5);
    }
}

三、源碼調(diào)試

要在主工程中進(jìn)行源碼調(diào)試,首先要有源碼,這邊要注意要和主工程依賴(lài)的版本對(duì)應(yīng)。工程依賴(lài)的三方庫(kù)都是不帶符號(hào)的,我們可以解壓APK查看libtest-lib.so,stripped表示調(diào)試信息已經(jīng)去掉了

1. 獲取符號(hào)文件

符號(hào)文件中包含了行號(hào)信息,這樣調(diào)試器就知道符號(hào)地址對(duì)應(yīng)的行號(hào),也包含源碼文件的信息,這樣在IDE中調(diào)試器就能調(diào)到對(duì)應(yīng)的文件中的某一行了。

在庫(kù)工程中,雖然發(fā)布的時(shí)候so去掉了調(diào)試信息,但是構(gòu)建時(shí)會(huì)生成cmake原始的構(gòu)建產(chǎn)物,其中帶有符號(hào)信息,注意到帶符號(hào)的so要比不帶符號(hào)的要大很多

2. 調(diào)試過(guò)程和原理

1)啟動(dòng)調(diào)試器

可通過(guò)Debug 'app'或者Attach Debugger to Process的方式,后者無(wú)需重啟應(yīng)用,注意選擇Debug Type為native,因?yàn)槲覀円{(diào)試的是native代碼。

2)設(shè)置斷點(diǎn)

調(diào)試器連接上進(jìn)程之后,點(diǎn)擊pause program,然后我們?cè)趈ni方法Java_smarttime_tsia_com_mylib_MyLibNative_nativeCalAverage設(shè)置一個(gè)斷點(diǎn),source info可以查看當(dāng)前堆棧的源碼和行號(hào)信息,但這時(shí)候我們還沒(méi)有符號(hào)文件,所以只能看到一個(gè)它的地址。

3)添加符號(hào)文件

add-dsym可以添加指定的符號(hào)文件,如果這時(shí)候libtest-lib.so還沒(méi)有加載進(jìn)來(lái)就會(huì)添加失敗,匹配不到對(duì)應(yīng)的模塊。

add-dsym的時(shí)候我們?nèi)〉檬莔ylib庫(kù)工程中cmake的構(gòu)建產(chǎn)物,現(xiàn)在我們將庫(kù)工程的源碼在主工程的IDE中打開(kāi)。這時(shí)我們通過(guò)IDE在文件中打斷點(diǎn)就能看到行號(hào)都已經(jīng)對(duì)上了。

4)觸發(fā)斷點(diǎn)

斷點(diǎn)觸發(fā)以后,source info查看到當(dāng)前的行號(hào)已經(jīng)有了,源碼信息也指向我們工程中打開(kāi)的源碼文件,所以IDE就跳到對(duì)應(yīng)的斷點(diǎn)上了,然后我們就可以進(jìn)行單步調(diào)試、條件斷點(diǎn)、watch point等調(diào)試了。

實(shí)際調(diào)試中只要調(diào)試器連接到進(jìn)程,然后添加調(diào)試符號(hào)就行了,然后Android Studio中在對(duì)應(yīng)的源碼文件中直接打斷點(diǎn)調(diào)試即可,上述過(guò)程只是方便分析原理。

3. 源碼映射

上述的符號(hào)文件和源碼是在庫(kù)工程里,符號(hào)文件是源碼構(gòu)建生成的,所以我們添加了符號(hào)文件就能調(diào)試源碼了。實(shí)際在很多項(xiàng)目中庫(kù)構(gòu)建和發(fā)布都是在一臺(tái)專(zhuān)門(mén)用來(lái)打包的服務(wù)器(云端)上進(jìn)行的,本地只有對(duì)應(yīng)版本的源碼,為了保證一致性,主工程依賴(lài)的是打包機(jī)器發(fā)布的庫(kù),我們也用打包機(jī)器上生成的符號(hào)文件來(lái)調(diào)試。
打包機(jī)器和本地生成的符號(hào)文件唯一的不同就是構(gòu)建環(huán)境,也就是源碼文件的路徑信息。我們來(lái)模擬一下,假設(shè)上述在mylib工程的目錄就是打包發(fā)布的專(zhuān)用目錄,而我們要調(diào)試的native代碼放在了另一個(gè)目錄里。

打包時(shí)環(huán)境:/Users/derek/Documents/AndroidProject/jniTest2/mylib/src/main/jni/test.cpp
調(diào)試源碼路徑:/Users/derek/Downloads/src/test.cpp

符號(hào)文件添加后,斷點(diǎn)到j(luò)ni方法,雖然有行號(hào)信息,但I(xiàn)DE中并沒(méi)有調(diào)到對(duì)應(yīng)的文件,因?yàn)檫@時(shí)候調(diào)試器只知道符號(hào)文件的源碼信息,它沒(méi)有指向本地要調(diào)試的源碼。

執(zhí)行LLDB的setting set target.source-map命令把符號(hào)文件中的源碼信息指向本地調(diào)試的文件路徑,設(shè)置成功后,可以看到斷點(diǎn)就生效了,這時(shí)候source info也改變了。

4. Android Studio Debugger配置

以上我們都是通過(guò)執(zhí)行l(wèi)ldb命令來(lái)添加符號(hào)、源碼映射的,如果覺(jué)得麻煩Android Studio也提供了Debugger配置:

Symbol Directories為符號(hào)目錄,調(diào)試器會(huì)遞歸的尋找這個(gè)目錄下的文件,直到匹配到需要的符號(hào)文件為止;LLDB Post Attach Commands為調(diào)試器連接到進(jìn)程后執(zhí)行的命令,我們可以在這里添加setting set target.source-map源碼映射的命令。

如果通過(guò)Debugger配置來(lái)調(diào)試,必須用Debug 'app'的方式,不能使用Attach的方式,因?yàn)橹挥星罢卟艜?huì)去執(zhí)行Debugger里的命令。

參考:

http://lldb.llvm.org/tutorial.html
https://developer.android.com/studio/run/rundebugconfig?hl=zh-cn#definingbefore

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

相關(guān)閱讀更多精彩內(nèi)容

  • 發(fā)現(xiàn) 關(guān)注 消息 iOS 第三方庫(kù)、插件、知名博客總結(jié) 作者大灰狼的小綿羊哥哥關(guān)注 2017.06.26 09:4...
    肇東周閱讀 15,670評(píng)論 4 61
  • 我們當(dāng)今的社會(huì)是互聯(lián)網(wǎng)時(shí)代,在互聯(lián)網(wǎng)熱火朝天的當(dāng)今,人們經(jīng)常談?wù)撊斯ぶ悄埽ˋrtificial Intellige...
    印第安南閱讀 403評(píng)論 0 1
  • 現(xiàn)在人真的會(huì)飛了 不是降落傘 是飛行翼 其實(shí)飛行翼也是降落傘的一種吧 如果真的是飛行器 是應(yīng)該有動(dòng)力裝置的 哪怕是...
    土山熊閱讀 163評(píng)論 0 0

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