調試項目
在構建應用后,您可能需要對其進行調試候引。本節(jié)介紹 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 中開發(fā)您的應用奋救。在 Windows 上岭参,使用 Cygwin 或實驗性
ndk-gdb-py
Python 實現。 - 使用 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 NDK/ndk-gdb
</pre>
此處利耍,$PROJECT
指向項目的根目錄蚌本,$NDK
指向 NDK 安裝路徑。
調用 ndk-gdb
時隘梨,它配置此會話以查找源文件和生成的原生庫的符號/調試版本程癌。 成功附加到您的應用進程后,ndk-gdb
將輸出一長串錯誤消息轴猎,表示其無法找到各種系統(tǒng)庫嵌莉。 這很正常,因為您的主機不包含您的目標設備上的這些庫的符號/調試版本捻脖。 您可以完全忽略這些消息锐峭。
接下來,ndk-gdb
顯示一個正常的 GDB 提示可婶。
您使用與 GNU GDB 交互的方式與 ndk-gdb
進行交互沿癞。例如,您可以使用 b <location>
設置斷點矛渴,使用 c
(表示“continue”)繼續(xù)執(zhí)行椎扬。 有關完整的命令列表,請參閱 GDB 手冊。
請注意蚕涤,當您退出 GDB 提示時筐赔,您正在調試的應用進程將停止。此行為是 gdb 的一個局限揖铜。
ndk-gdb
可處理許多錯誤情況茴丰,如果它發(fā)現問題,將顯示信息性錯誤消息天吓。這些檢查包括確保符合以下條件:
- 確保 ADB 位于您的路徑中较沪。
- 確保您的應用在其清單中聲明為可調試。
- 確保設備上安裝的具有相同軟件包名稱的應用同樣可調試失仁。
默認情況下尸曼,ndk-gdb
搜索一個已運行的應用進程,如果未找到萄焦,則顯示錯誤控轿。 不過,您可以使用 --start
或 --launch=<name>
選項在調試會話前自動啟動您的 Activity拂封。 如需了解詳細信息茬射,請參閱選項。
選項
若要查看完整的選項列表冒签,請在命令行上鍵入 ndk-gdb --help
在抛。表 1 顯示了許多比較常用的選項及其簡要說明。
表 1. 常用 ndk-gdb 選項及其說明萧恕。
通過這個指定的選項啟動 ndk-gdb
將啟動應用清單中列出的第一個可啟動 Activity刚梭。 使用 --launch=<name>
來啟動下一個可啟動 Activity。 若要轉儲可啟動 Activity 的列表票唆,請從命令行運行 --launch-list
朴读。
| 選項 | 說明> |
| --verbose
|
此選項指示構建系統(tǒng)打印有關原生調試會話設置的詳細信息。 僅在調試程序無法連接到應用走趋,且 ndk-gdb
顯示的錯誤消息不充分時才需要用它調試問題衅金。
|
| --force
| 默認情況下,如果 ndk-gdb
發(fā)現另一個原生調試會話已在相同設備上運行簿煌,它將會取消運行氮唯。 此選項將終止另一個會話,并將其替換為新的會話姨伟。 請注意惩琉,此選項不會終止正在調試的實際應用,您必須另行終止它授滓。 |
| --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 工具可執(zhí)行文件乡翅。 只有在您未設置包括該可執(zhí)行文件的路徑時才需要使用此選項鳞疲。
|
| * -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
環(huán)境變量以列出特定的設備,無需具體的選項凛忿。
|
| * --exec=<file>
* -x <file>
|
此選項指示 ndk-gdb
在連接至它正在調試的進程后運行在 <file>
中找到的 GDB 初始化命令澈灼。 如果您要重復執(zhí)行某些操作,如設置斷點列表店溢,然后繼續(xù)自動執(zhí)行叁熔,則該功能很有用。
|
| --nowait
|
停用暫停 Java 代碼床牧,直到連上 GDB荣回。傳遞此選項可能會使調試程序錯過早期的斷點。
|
| --tui
-t
|
啟用 Text User Interface(如果可用)戈咳。
|
| --gnumake-flag=<flag>
|
此選項是在查詢 ndk-build
系統(tǒng)以獲取項目信息時要傳遞到該系統(tǒng)的額外標志(或多個標志)心软。 您可以在同一個命令中使用此選項的多個實例壕吹。
|
| --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
文件運行删铃,因此耳贬,該模式僅適用于共享庫。 以靜態(tài)方式鏈接到 libstdc++
庫時猎唁,您必須指定所需的打印機咒劲。 默認值為 none
。
|
注:此表中的最后三個選項僅適用于 ndk-gdb
的 Python 版本诫隅。
線程支持
如果您的應用在 Android 2.3(API 級別 9)以前的版本上運行腐魂,則 ndk-gdb
無法正確調試原生線程。 調試程序只能調試主線程逐纬,abd 完全忽略其他線程的執(zhí)行蛔屹。
如果您在非主線程上執(zhí)行的函數上放置一個斷點,則程序將退出豁生,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: "Roboto Mono", 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: "Roboto Mono", 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 構建系統(tǒng) (ndk-build
)围小,則這些共享庫文件位于 $PROJECT_PATH/obj/local/<abi>
下昵骤,其中 <abi>
表示您的設備的 ABI。 默認情況下肯适,系統(tǒng)使用 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 | PROJECT_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
PROJECT_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
無法正常工作。