使用Visual Studio 2017开发android ndk一文中已经讲了如何使用Visual Studio高效的进行ndk开发,那么问题来了,使用Visual Studio确实能加快代码的编写速度,但是由于程序是运行在android手机上的,如何进行debug便成了问题。

以前debug都是使用GDB,这个工具确实不好用,Android Studio 2.2版本带来了全新的对Android Native代码的开发以及调试支持,另外LLDB的Android调试插件也日渐成熟,安装LLDB+LLVM插件后,Android Studio对C/C++进行调试将是主流。

1.安装LLDB等插件

从SDK Manager中安装CMake,LLDB,NDK,其中ndk版本要大于r12。

android app调用so编译 android调试so库_android app调用so编译

android app调用so编译 android调试so库_符号表_02

2.打开工程Native调试

第一步:在build.gradle文件debug编译类型中添加debuggable true和jniDebuggable true两个设置

buildTypes {
        debug {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
            debuggable true
            jniDebuggable true
        }
    }

或者直接中项目设置中打开,效果是一样的。

工程右键->open Module Setting->

android app调用so编译 android调试so库_android app调用so编译_03

第二步:使用native调试方式启动程序

android app调用so编译 android调试so库_Android_04

android app调用so编译 android调试so库_android app调用so编译_05

第三步:使用debug按钮启动程序

android app调用so编译 android调试so库_Android_06

注意:必须使用debug按钮启动程序,java调试时可以在程序启动后点击

android app调用so编译 android调试so库_android_07

Attach debugger按钮再进行调试,native的调试不可以使用此方法。这是因为使用Native的调试方式启动程序是需要启动在手机上启动lldb-server程序,并进程端口映射和远程连接。

3.设置LLDB参数

对so进行调试必须要解决两个问题:

1.要提供带有符号表so;

2.如何将符号表so与源码关联。

问题一:

我们编译出的含有so的文件夹有两个,其中libs目录中为无调试符号的so,obj目录下为带调试符号的so。

android app调用so编译 android调试so库_android_08

如果调试,我们需要指定带符号表的so所在的位置,根据目录结构,我们将符号表目录指向到obj\local目录。

这样就完成了带符号表的so与无符号表so的映射。

android app调用so编译 android调试so库_android app调用so编译_09

android app调用so编译 android调试so库_android_10

问题二:

带符号表的so中会携带编译时文件在文件系统中的绝对路径,如果是本机编译+本机AS调试,则无需进行源码路径的映射,Android Studio会自动帮你完成映射,但如果so并非本机编译,则需要将so中源码路径映射到本机的源码路径。

进行映射前,我们必须先知道带符号的so中记录的文件路径。

安装启动调试:

android app调用so编译 android调试so库_android_11

程序启动后,打开debug标签,激活lldb命令窗口

android app调用so编译 android调试so库_Test_12

android app调用so编译 android调试so库_符号表_13

这里介绍一个LLDB添加断点的命令:

下面三句功能是一样的,都是向Test.cpp文件的第14行添加断点,只不过是不同的简写版本。

(lldb) breakpoint set --file Test.cpp --line 14
(lldb) br s -f Test.cpp -l 14
(lldb) b Test.cpp:14

再看一眼我们的测试代码

android app调用so编译 android调试so库_Test_14

添加断点效果如下,说明断点添加成功。

android app调用so编译 android调试so库_Android_15

放开程序,让程序继续执行:

android app调用so编译 android调试so库_android app调用so编译_16

执行一个可以触发断点的操作,让程序进入刚刚添加的断点,进入断点后,切换到Variables窗口,就可以看到实时变量了。

android app调用so编译 android调试so库_符号表_17

再切换回LLDB窗口,输入source info命令就可以看到断点文件的路径了

(lldb) source info
Lines found in module `libtest.so
[0x0000007fa59025e0-0x0000007fa59025ec): E:\workspace\viso_studio_workspace\Android\Project1\Project1\.\.\Test.cpp:14

这里我们已经知道带有符号表的文件路径是E:\workspace\viso_studio_workspace\Android\Project1\Project1,

而我们本地源码目录为C:\Users\guozr\source\repos\Project2\Project2,故我们需要将Project1目录映射到Project2目录。

这里又要介绍一个映射目录的命令:

命令第一个路径为so构建路径,第二个命令为本机源码路径。

(lldb) settings set target.source-map /buildbot/path /my/path

这里我们需要替换为如下命令:

(lldb) settings set target.source-map E:\workspace\viso_studio_workspace\Android\Project1\Project1 C:\Users\guozr\source\repos\Project2\Project2

这个命令我们可以在程序启动后,使用类似前面添加断点的方式设置,也可以通过as提供的LLDB Startup Commands选项卡设置,这样调试程序一启动就完成了目录映射。

如下:

android app调用so编译 android调试so库_符号表_18

通过上面的设置,我们已经将源文件关联到了带符号表的so中,由于Android Studio对Native调试的支持,现在我们就可以将关联的c++源文件直接拖到Android Studio中打开,然后直接使用Android Studio编辑器界面中左边面板直接设置和去掉断点了。

android app调用so编译 android调试so库_Test_19

更多LLDB命令请参考

GDB to LLDB command map — The LLDB Debugger

补充:

在很多情况下,我们app会分成很多不同的module,所以经常会出现将还有native代码的so库文件放在lib module中,这时我们就会遇到native代码无法debug的问题。

这时我们首先想到的是开启lib module的debug,

debug {
    debuggable true
    jniDebuggable true
}

然而你开启这个选项后仍然无法debug,

通过编译时查看gradle日志可以看出,通过compile project(":library")的方式,打包到app内的lib module实际是release方式编译的,即使你app上选择的是debug方式打包,lib module参与打包的方式仍然是release方式,由于release是不能debug的,所以你一直无法debug。

......
:library:compileReleaseJavaWithJavac UP-TO-DATE
:library:extractReleaseAnnotations UP-TO-DATE
:library:mergeReleaseShaders UP-TO-DATE
:library:compileReleaseShaders UP-TO-DATE
:library:generateReleaseAssets UP-TO-DATE
:library:mergeReleaseAssets UP-TO-DATE
:library:mergeReleaseProguardFiles UP-TO-DATE
:library:packageReleaseRenderscript UP-TO-DATE
:library:packageReleaseResources UP-TO-DATE
:library:processReleaseJavaRes UP-TO-DATE
:library:transformResourcesWithMergeJavaResForRelease UP-TO-DATE
:library:transformClassesAndResourcesWithSyncLibJarsForRelease UP-TO-DATE
:library:mergeReleaseJniLibFolders UP-TO-DATE
:library:transformNative_libsWithMergeJniLibsForRelease UP-TO-DATE
:library:transformNative_libsWithSyncJniLibsForRelease UP-TO-DATE
:library:bundleRelease UP-TO-DATE
......
:app:validateSigningDebug
:app:packageDebug UP-TO-DATE
:app:assembleDebug UP-TO-DATE

BUILD SUCCESSFUL

Total time: 11.475 secs

因此,要想debug,必须让lib module以debug的方式参与打包,方式如下。

在Library工程gradle中添加publishNonDefault true标识:

android {
    publishNonDefault true
}

在app的gradle文件中使用如下方式添加依赖:

debugCompile project(path: ':library', configuration: 'debug')
releaseCompile project(path: ':library', configuration: 'release')

好的,接下来继续你的表演吧。