android ndk 调试项目

调试项目

在构建应用后,您可能需要对其进行调试。本节介绍 NDK 的调试工具。

首先介绍如何使用 ndk-gdb 工具调试代码。 最后说明 ndk-stack 工具,该工具可帮助您在调试时使用 ADB logcat 工具

ndk-gdb

NDK 包含一个名为 ndk-gdb 的帮助程序 shell 脚本,可轻松地为 NDK 生成的机器代码启动原生调试会话。

要求

要运行原生调试,您必须遵循以下要求:

  • 使用 ndk-build 脚本构建您的应用。ndk-gdb 脚本不支持使用旧的 make APP=<name>方法进行构建。
  • 通过添加一个将 android:debuggable 属性设置为 true<application> 元素,在 AndroidManifest.xml 文件中启用应用调试。
  • 构建在 Android 2.2(Android API 级别 8)或更高版本上运行的应用。
  • 在运行 Android 2.2 或更高版本的设备或模拟器上进行调试。 在 AndroidManifest.xml文件中声明的 API 级别对于调试并不重要。
  • 在 Unix shell 中开发您的应用。在 Windows 上,使用 Cygwin 或实验性 ndk-gdb-pyPython 实现。
  • 使用 GNU Make 3.81 或更高版本。

用法

如需调用 ndk-gdb 脚本,切换到应用目录或该目录下面的任何目录。 例如:

<pre class="no-pretty-print" style="box-sizing: inherit; background: rgb(247, 247, 247); color: rgb(55, 71, 79); font-style: normal; font-variant: normal; font-weight: normal; font-stretch: normal; font-size: 14px; line-height: 20px; font-family: "Roboto Mono", monospace; padding: 8px; margin: 16px 0px; overflow-x: auto; position: relative;">cd PROJECTNDK/ndk-gdb
</pre>

此处,$PROJECT 指向项目的根目录,$NDK 指向 NDK 安装路径。

调用 ndk-gdb 时,它配置此会话以查找源文件和生成的原生库的符号/调试版本。 成功附加到您的应用进程后,ndk-gdb 将输出一长串错误消息,表示其无法找到各种系统库。 这很正常,因为您的主机不包含您的目标设备上的这些库的符号/调试版本。 您可以完全忽略这些消息。

接下来,ndk-gdb 显示一个正常的 GDB 提示。

您使用与 GNU GDB 交互的方式与 ndk-gdb 进行交互。例如,您可以使用 b <location> 设置断点,使用 c(表示“continue”)继续执行。 有关完整的命令列表,请参阅 GDB 手册

请注意,当您退出 GDB 提示时,您正在调试的应用进程将停止。此行为是 gdb 的一个局限。

ndk-gdb 可处理许多错误情况,如果它发现问题,将显示信息性错误消息。这些检查包括确保符合以下条件:

  • 确保 ADB 位于您的路径中。
  • 确保您的应用在其清单中声明为可调试。
  • 确保设备上安装的具有相同软件包名称的应用同样可调试。

默认情况下,ndk-gdb 搜索一个已运行的应用进程,如果未找到,则显示错误。 不过,您可以使用 --start--launch=<name> 选项在调试会话前自动启动您的 Activity。 如需了解详细信息,请参阅选项

选项

若要查看完整的选项列表,请在命令行上键入 ndk-gdb --help。表 1 显示了许多比较常用的选项及其简要说明。

表 1. 常用 ndk-gdb 选项及其说明。

通过这个指定的选项启动 ndk-gdb 将启动应用清单中列出的第一个可启动 Activity。 使用 --launch=<name> 来启动下一个可启动 Activity。 若要转储可启动 Activity 的列表,请从命令行运行 --launch-list

| 选项 | 说明> |
| --verbose |

此选项指示构建系统打印有关原生调试会话设置的详细信息。 仅在调试程序无法连接到应用,且 ndk-gdb 显示的错误消息不充分时才需要用它调试问题。

|
| --force | 默认情况下,如果 ndk-gdb 发现另一个原生调试会话已在相同设备上运行,它将会取消运行。 此选项将终止另一个会话,并将其替换为新的会话。 请注意,此选项不会终止正在调试的实际应用,您必须另行终止它。 |
| --start |

当您启动 ndk-gdb 时,默认情况下,它尝试附加到目标设备上您的应用已运行的实例。 您可以替换此默认行为,在调试会话前使用 --start 在目标设备上显式启动应用。

|
| --launch=<name> |

此选项类似于 --start,不过它允许您从应用启动特定 Activity。 仅当您的清单定义多个可启动 Activity 时,才可使用该功能。

|
| --launch-list |

这个便捷选项打印在您的应用清单中找到的所有可启动 Activity 名称的列表。--start 使用第一个 Activity 名称。

|
| --project=<path> | 此选项指定应用项目目录。如果您希望不必先切换到项目目录就可启动脚本,则该选项很有用。 |
| --port=<port> |

默认情况下,ndk-gdb 使用本地 TCP 端口 5039 与它在目标设备上调试的应用进行通信。 使用不同的端口让您可以在本地调试连接至相同主机的不同设备或模拟器上运行的程序。

|
| --adb=<file> |

此选项指定 adb 工具可执行文件。 只有在您未设置包括该可执行文件的路径时才需要使用此选项。

|
| * -d* -e* -s <serial> |

这些标志与具有相同名称的 adb 命令类似。如果您有连接至主机的多个设备或模拟器,请设置这些标志。 其含义如下所示:

<dl style="box-sizing: inherit; margin: 0px; padding: 0px;">

<dt style="box-sizing: inherit; font-style: normal; font-variant: normal; font-weight: 700; font-stretch: normal; font-size: 16px; line-height: 24px; font-family: Roboto, sans-serif; margin: 16px 0px;">-d</dt>

<dd style="box-sizing: inherit; margin: 16px 0px; padding: 0px 0px 0px 20px;">连接至单个物理设备。</dd>

<dt style="box-sizing: inherit; font-style: normal; font-variant: normal; font-weight: 700; font-stretch: normal; font-size: 16px; line-height: 24px; font-family: Roboto, sans-serif; margin: 16px 0px;">-e</dt>

<dd style="box-sizing: inherit; margin: 16px 0px; padding: 0px 0px 0px 20px;">连接至单个模拟器设备。</dd>

<dt style="box-sizing: inherit; font-style: normal; font-variant: normal; font-weight: 700; font-stretch: normal; font-size: 16px; line-height: 24px; font-family: Roboto, sans-serif; margin: 16px 0px;">-s <serial></dt>

<dd style="box-sizing: inherit; margin: 16px 0px; padding: 0px 0px 0px 20px;">连接至特定设备或模拟器。此处,<serial> 是设备的名称(如 adb devices 命令所列出)。</dd>

</dl>

或者,您可以定义 ADB_SERIAL 环境变量以列出特定的设备,无需具体的选项。

|
| * --exec=<file>* -x <file> |

此选项指示 ndk-gdb 在连接至它正在调试的进程后运行在 <file> 中找到的 GDB 初始化命令。 如果您要重复执行某些操作,如设置断点列表,然后继续自动执行,则该功能很有用。

|
| --nowait |

停用暂停 Java 代码,直到连上 GDB。传递此选项可能会使调试程序错过早期的断点。

|
| --tui -t |

启用 Text User Interface(如果可用)。

|
| --gnumake-flag=<flag> |

此选项是在查询 ndk-build 系统以获取项目信息时要传递到该系统的额外标志(或多个标志)。 您可以在同一个命令中使用此选项的多个实例。

|
| --stdcxx-py-pr={auto|<wbr style="box-sizing: inherit;">none|<wbr style="box-sizing: inherit;">gnustdcxx[-GCCVER]|<wbr style="box-sizing: inherit;">stlport} |

在 C++ 标准库中使用指定的 Python pretty-printer 显示相应类型。auto 模式通过查看用于 libstdc++ 库的.so 文件运行,因此,该模式仅适用于共享库。 以静态方式链接到 libstdc++ 库时,您必须指定所需的打印机。 默认值为 none

|

注:此表中的最后三个选项仅适用于 ndk-gdb 的 Python 版本。

线程支持

如果您的应用在 Android 2.3(API 级别 9)以前的版本上运行,则 ndk-gdb 无法正确调试原生线程。 调试程序只能调试主线程,abd 完全忽略其他线程的执行。

如果您在非主线程上执行的函数上放置一个断点,则程序将退出,GDB 将显示以下消息。

<pre class="no-pretty-print" style="box-sizing: inherit; background: rgb(247, 247, 247); color: rgb(55, 71, 79); font-style: normal; font-variant: normal; font-weight: normal; font-stretch: normal; font-size: 14px; line-height: 20px; font-family: "Roboto Mono", monospace; padding: 8px; margin: 16px 0px; overflow-x: auto; position: relative;">Program terminated with signal SIGTRAP, Trace/breakpoint trap.
The program no longer exists.</pre>

ndk-stack

ndk-stack 工具让您可以在堆叠追踪出现在 adb logcat 的输出中时过滤它们。 它还可以从源代码将共享库中的任意地址替换为对应的 <source-file>:<line-number> 值,从而更容易找出问题所在。

例如,它可将下面的内容:

<pre class="prettyprint" style="box-sizing: inherit; background: rgb(247, 247, 247); color: rgb(55, 71, 79); font-style: normal; font-variant: normal; font-weight: normal; font-stretch: normal; font-size: 14px; line-height: 20px; font-family: &quot;Roboto Mono&quot;, monospace; padding: 8px; margin: 16px 0px; overflow-x: auto; position: relative;">I/DEBUG (  31):  ***  ***  ***  ***  ***  ***  ***  ***  ***  ***  ***  ***  ***  ***  ***  *** I/DEBUG (  31):  Build fingerprint:  'generic/google_sdk/generic/:2.2/FRF91/43546:eng/test-keys' I/DEBUG (  31): pid:  351, tid:  351 %gt;%gt;%gt;  /data/local/ndk-tests/crasher <<< I/DEBUG (  31): signal 11  (SIGSEGV), fault addr 0d9f00d8 I/DEBUG (  31): r0 0000af88 r1 0000a008 r2 baadf00d  r3 0d9f00d8 I/DEBUG (  31): r4 00000004 r5 0000a008 r6 0000af88 r7 00013c44 I/DEBUG (  31): r8 00000000 r9 00000000 10  00000000 fp 00000000 I/DEBUG (  31): ip 0000959c sp be956cc8  lr 00008403 pc 0000841e cpsr 60000030 I/DEBUG (  31): #00  pc 0000841e  /data/local/ndk-tests/crasher I/DEBUG (  31): #01  pc 000083fe  /data/local/ndk-tests/crasher I/DEBUG (  31): #02  pc 000083f6  /data/local/ndk-tests/crasher I/DEBUG (  31): #03  pc 000191ac  /system/lib/libc.so I/DEBUG (  31): #04  pc 000083ea  /data/local/ndk-tests/crasher I/DEBUG (  31): #05  pc 00008458  /data/local/ndk-tests/crasher I/DEBUG (  31): #06  pc 0000d362  /system/lib/libc.so I/DEBUG (  31):</pre>

转换为更容易阅读的输出:

<pre class="prettyprint" style="box-sizing: inherit; background: rgb(247, 247, 247); color: rgb(55, 71, 79); font-style: normal; font-variant: normal; font-weight: normal; font-stretch: normal; font-size: 14px; line-height: 20px; font-family: &quot;Roboto Mono&quot;, monospace; padding: 8px; margin: 16px 0px; overflow-x: auto; position: relative;">**********  Crash  dump:  **********  Build fingerprint:  'generic/google_sdk/generic/:2.2/FRF91/43546:eng/test-keys' pid:  351, tid:  351 >>>  /data/local/ndk-tests/crasher <<< signal 11  (SIGSEGV), fault addr 0d9f00d8  Stack frame #00  pc 0000841e  /data/local/ndk-tests/crasher : Routine zoo in /tmp/foo/crasher/jni/zoo.c:13  Stack frame #01  pc 000083fe  /data/local/ndk-tests/crasher : Routine bar in /tmp/foo/crasher/jni/bar.c:5  Stack frame #02  pc 000083f6  /data/local/ndk-tests/crasher : Routine my_comparison in /tmp/foo/crasher/jni/foo.c:9  Stack frame #03  pc 000191ac  /system/lib/libc.so  Stack frame #04  pc 000083ea  /data/local/ndk-tests/crasher : Routine foo in /tmp/foo/crasher/jni/foo.c:14  Stack frame #05  pc 00008458  /data/local/ndk-tests/crasher : Routine main in /tmp/foo/crasher/jni/main.c:19  Stack frame #06  pc 0000d362  /system/lib/libc.so</pre>

用法

若要使用 ndk-stack,首先,您需要一个包含应用共享库的符号版本的目录。 如果您使用 NDK 构建系统 (ndk-build),则这些共享库文件位于 $PROJECT_PATH/obj/local/<abi>下,其中 <abi> 表示您的设备的 ABI。 默认情况下,系统使用 armeabi ABI。

可通过两种方式使用此工具。您可以将 logcat 文本作为直接输入馈送到程序。例如:

<pre class="no-pretty-print" style="box-sizing: inherit; background: rgb(247, 247, 247); color: rgb(55, 71, 79); font-style: normal; font-variant: normal; font-weight: normal; font-stretch: normal; font-size: 14px; line-height: 20px; font-family: "Roboto Mono", monospace; padding: 8px; margin: 16px 0px; overflow-x: auto; position: relative;">adb logcat | NDK/ndk-stack -symPROJECT_PATH/obj/local/armeabi
</pre>

您也可以使用 -dump 选项将 logcat 指定为输入文件。例如:

<pre class="no-pretty-print" style="box-sizing: inherit; background: rgb(247, 247, 247); color: rgb(55, 71, 79); font-style: normal; font-variant: normal; font-weight: normal; font-stretch: normal; font-size: 14px; line-height: 20px; font-family: "Roboto Mono", monospace; padding: 8px; margin: 16px 0px; overflow-x: auto; position: relative;">adb logcat > /tmp/foo.txt
NDK/ndk-stack -symPROJECT_PATH/obj/local/armeabi -dump foo.txt
</pre>

该工具在开始解析 logcat 输出时将查找第一行星号。例如:

<pre class="no-pretty-print" style="box-sizing: inherit; background: rgb(247, 247, 247); color: rgb(55, 71, 79); font-style: normal; font-variant: normal; font-weight: normal; font-stretch: normal; font-size: 14px; line-height: 20px; font-family: "Roboto Mono", monospace; padding: 8px; margin: 16px 0px; overflow-x: auto; position: relative;">*** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
</pre>

注:在复制/粘贴追踪时,请别忘了此行,否则 ndk-stack 无法正常工作。

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

推荐阅读更多精彩内容