Andorid Studio NDK 开发 - LLDB 调试

GastonIXE 8年前
   <p>LLDB 是一个高效的 c/c++ 的调试器,是与 LLVM编译器 一起使用,提供了丰富的流程控制和数据检测,有效的帮忙我们调试程序。 LLDB 也已经取代GDB成为XCode的默认调试器, Android Studio 中也可以使用 LLDB 调试NDK程序,在 Android Studio 也中可以 LLDB ,从 SDK Tools 中下载 LLDB 最新版本,配合 Android Studio 和 gradle-experimental 一起调试NDK项目,会更加的方便。</p>    <h2>LLDB安装</h2>    <p>在 Androis Studio 的 Toolbar 中可以找到Android的更新图标,打开可以看到Android SDK的升级配置,在 SDK Tools 中可以找到 LLDB 的安装选项。</p>    <p><img src="https://simg.open-open.com/show/e9dc3c72a57783771e099dfb095386f8.png"></p>    <p>安装LLDB</p>    <p>究竟如何使用 LLDB 调试NDK程序呢?在上一篇的 <a href="/misc/goto?guid=4959746227794157230" rel="nofollow,noindex">Andorid Studio NDK 开发 - NDK 开发利器 gradle-experimental</a> 中介绍了使用 gradle-experimental 可以简化NDK的开发配置,其中提到了在运行选项中有两个运行的配置选项 app 和 app-native ,其中的app-native就是用来运行和调试JNI开发的。 app-native 不仅仅可以直接运行,也可以进行Debug,选中 aap-native 之后,直接选择debug按钮就可以进入NDK的Debug模式</p>    <h2>Debug JNI</h2>    <p>选择 app-native ,点击debug按钮可以直接进入Debug状态,在一段代码处设置一个断点,如图所示:</p>    <p><img src="https://simg.open-open.com/show/40665bfed3501445b629144764a2ccda.png"></p>    <p>断点</p>    <p>可以看到程序运行到断点出,就进入了Debug状态,在左侧的状态里面可以看到变量的值和指针地址:</p>    <p><img src="https://simg.open-open.com/show/11ae25151206d76300dcada7ba8c8a0f.png"></p>    <p>变量信息</p>    <p>程序进入了Debugz状态,但是并没有使用到 LLDB ,下面就使用下 LLDB</p>    <p>强大的功能。</p>    <h2>使用LLDB</h2>    <p>从上面的图中可以看到除了 Variables 的Tab页以外,还有一个Tab页就是 LLDB ,点击进入可以看到 (lldb) 的命令行,在命令行里面可以输入 LLDB 的命令, LLDB 命令有很多强大的能力,比如,打印,寻址,调用堆栈等,通过这些命令可以有效的帮助调试NDK程序。</p>    <p><img src="https://simg.open-open.com/show/5cf59a9b0aa8b5e0f4f37301d0a0d01f.png"></p>    <p>lldb</p>    <p>上图中使用p(print)打印命令打印出了 chars</p>    <p>变量的内容,打印的内容包括变量的类型:char[10],变量的值:"i am test"。</p>    <h2>LLDB常用命令</h2>    <ul>     <li> <p>print (p)打印命令,打印变量以及其值:</p> <p>p chars</p> <p>(char [10]) $0 = "i am test"</p> </li>     <li> <p>po 仅打印变量的值:</p> <p>po chars</p> <p>"i am test"</p> </li>     <li> <p>call 就是调用的意思,上述po和p也有调用的功能。一般只在不需要显示输出,或是方法无返回值时使用。例如定义一个变量int p=0,使用 call 命令:</p> <p>call p++</p> <p>(int) $0 = 1</p> </li>     <li> <p>expr 可以在调试时动态执行指定表达式,并打印结果,用于在调试过程中修改变量的值</p> <p>比如我们在程序里面定义int b=1;可以在断点的时候使用 expr 更改其值。</p> <p>expr b=10</p> <p>(int) $0 = 10</p> </li>    </ul>    <ul>     <li> <p>bt 命令用来堆栈信息,加all可打印所有thread的堆栈,比如我们增加一段导致崩溃的代码</p> <pre>  <code class="language-cpp">JNIEXPORT jstring JNICALL  Java_com_jjz_NativeUtil_firstNative(JNIEnv *env, jclass type) {    char chars[] = "i am test";    int p=0;    p=1/0;    return (*env)->NewStringUTF(env, chars);  }</code></pre> <p>运行的时候出现了崩溃,这个时候运行 bt 命令,得到堆栈信息:</p> <pre>  <code class="language-cpp">*thread #1, name = 'com.jjz', stop reason = signal SIGFPE  * frame #0: 0xf6ff35e8 libc.so`tgkill + 12    frame #1: 0xf6ff11ec libc.so`pthread_kill + 36    frame #2: 0xf6fcd846 libc.so`raise + 14    frame #3: libexperiment.so`__aeabi_idiv0 at lib1funcs.S:1337    frame #4: libexperiment.so`Java_com_jjz_NativeUtil_firstNative(env=0xf4218ea0, type=0xff8b732c) at experiment.c:16    frame #5: base.odex`java.lang.String com.jjz.NativeUtil.firstNative() at NativeUtil.java:0    frame #6: base.odex`void com.jjz.MainActivity.onCreate(android.os.Bundle) at MainActivity.java:15    frame #7: 0x729d58bf</code></pre> </li>     <li> <p>image用于寻址,可以用来查找栈地址对应的代码位置,image命令有很多的参数,可以帮助我们更加清晰的定位问题,其中比较常用的是 image lookup :</p> <p>image lookup --address 0xfxxxxx</p> <p>0xfxxxxx 表示对应的栈地址,有了地址可以通过 image lookup 命令来查看该栈地址上面对应的代码。</p> </li>    </ul>    <p> </p>    <p>来自:https://juejin.im/post/58dae3a5ac502e0058ee242f</p>    <p> </p>