在使用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。
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->
第二步:使用native调试方式启动程序
第三步:使用debug按钮启动程序
注意:必须使用debug按钮启动程序,java调试时可以在程序启动后点击Attach debugger按钮再进行调试,native的调试不可以使用此方法。这是因为使用Native的调试方式启动程序是需要启动在手机上启动lldb-server程序,并进程端口映射和远程连接。
3.设置LLDB参数
对so进行调试必须要解决两个问题:
1.要提供带有符号表so;
2.如何将符号表so与源码关联。
问题一:
我们编译出的含有so的文件夹有两个,其中libs目录中为无调试符号的so,obj目录下为带调试符号的so。
如果调试,我们需要指定带符号表的so所在的位置,根据目录结构,我们将符号表目录指向到obj\local目录。
这样就完成了带符号表的so与无符号表so的映射。
问题二:
带符号表的so中会携带编译时文件在文件系统中的绝对路径,如果是本机编译+本机AS调试,则无需进行源码路径的映射,Android Studio会自动帮你完成映射,但如果so并非本机编译,则需要将so中源码路径映射到本机的源码路径。
进行映射前,我们必须先知道带符号的so中记录的文件路径。
安装启动调试:
程序启动后,打开debug标签,激活lldb命令窗口
这里介绍一个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
再看一眼我们的测试代码
添加断点效果如下,说明断点添加成功。
放开程序,让程序继续执行:
执行一个可以触发断点的操作,让程序进入刚刚添加的断点,进入断点后,切换到Variables窗口,就可以看到实时变量了。
再切换回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选项卡设置,这样调试程序一启动就完成了目录映射。
如下:
通过上面的设置,我们已经将源文件关联到了带符号表的so中,由于Android Studio对Native调试的支持,现在我们就可以将关联的c++源文件直接拖到Android Studio中打开,然后直接使用Android Studio编辑器界面中左边面板直接设置和去掉断点了。
更多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')
好的,接下来继续你的表演吧。