Android環境下的GDB除錯
gdb是GNU開發的針對Linux/Unix環境下程式的除錯工具。為了節約目標系統的資源,gdb通常採用gdb+gdbserver的方式進行除錯。
在Android GDB除錯場景下,gdb執行在PC端,gdbserver執行在Android系統中。在實際的除錯過程中,PC端的gdb參照除錯符號檔案向gdbserver發出命令,gdbserver就會向執行程式發出訊號,從而實現對Android系統執行程式的跟蹤與控制:
- 啟動尚未執行C/C++程式,或者繫結到正在執行的C/C++程式
- 讓被除錯的程式在指定的斷點處中止執行
- 檢視當前中止狀態下的對用堆疊,區域性變數等資訊
- 通過對變數賦值,呼叫函式等操作來動態的改變程式的執行環境
除錯環境準備
gdb
gdb執行在PC端,通常位於Android原始碼中的prebuilt目錄下。不同的專案,不同的平臺,gdb的位置可能不同,但是我們可以在進入prebuilt目錄下通過find命令來查詢:find ./ -name “gdb“,常見路徑列舉如下:
prebuilts/gdb/linux-x86/bin/gdb
prebuilts/gcc/linux-x86/aarch64/aarch64-linux-android-4.9/bin/aarch64-linux-android-gdbgdbserver
gdbserver執行在Android系統中,預設已經安裝在eng或者userdebug的軟體版本中;根據被除錯程式的執行位數(32位/64位)的不同,gdbserver需要選擇相匹配的版本gdbserver/gdbserver64。
注意:如果手機中找不到gdbserver,我們可以將原始碼prebuilt目錄下的gdbserver安裝到手機中帶有除錯符號資訊的檔案
要除錯C/C++程式,必須要有除錯符號資訊。按照Android原始碼的編譯規則,除錯符號資訊通常位於在目錄out/target/product/[PRODUCT_NAME]/sysmbols下;為了能夠在除錯過程中獲取到更多的除錯資訊,建議對原始碼進行全編,並在除錯目標的Android.mk中關閉編譯優化:
LOCAL_CFLAGS += -O0 -g // -O0:優化級別為0, -g:開啟除錯
除錯舉例
以mediaserver程序為例;
Step 1. 啟動gdbserver,並繫結到mediaserver的程序上;
adb shell gdbserver :8888 --attach 1232
注意:
a. 使用gdbserver,還是使用gdbserver64,需要依據程序的位數來決定;程序的位數可以通過file命令檢視:
adb shell file system/bin/mediaserver
b. 1232為程序id,可以ps命令檢視;
adb shell ps -ef | grep mediaserver
c. 如果不繫結程序id,則gdbserver會重新建立一個新的程序:
adb shell gdbserver :8888 system/bin/mediaserver
建立的mediaserver相比於系統的啟動方法,會丟失一些group,selinux context資訊。因此如果系統已經啟動了除錯的程序,可以直接繫結到指定PID上;否則才可以考慮通過gdbserver建立新的程序;
Step 2. 將PC指定埠對映到Android系統中的指定埠上
adb forward tcp:8888 tcp:8888
Step 3. PC端啟動gdb client
prebuilts/gdb/linux-x86/bin/gdb out/target/product/[PRODUCT_NAME]/symbols/system/bin/mediaserver
Step 4. 指定含有除錯符號檔案的symbols路徑
(gdb) set solib-absolute-prefix out/target/product/[PRODUCT_NAME]/symbols/
(gdb) set solib-search-path out/target/product/[PRODUCT_NAME]/symbols/
Step 5. 將gdb與已啟動的gdbserver通過tcp/port相連線
(gdb)target remote :8888
Step 6. 開始各種姿勢的除錯
1. 顯示被除錯程式的資訊
(gdb) info thread: 列印當前程序所有的thread資訊
(gdb) info breakpoints [n] 顯示所有斷點(或斷點n)資訊
(gdb) info watchpoints [n] 顯示所有觀察點(或觀察點n)資訊
(gdb) info program 檢視被除錯程式的執行狀態
(gdb) info args 打印出當前函式的引數名及其值
(gdb) info locals 打印出當前函式中所有區域性變數及其值
(gdb) info display 檢視display設定的自動顯示的資訊
(gdb) info frame 檢視棧幀資訊,包括程式語言
(gdb) print a 列印變數a的值
(gdb) print &a 列印變數a的地址
2. 執行緒資訊除錯
(gdb) thread: 列印當前gdbserver所跟蹤的thread資訊
(gdb) thread [Id]: 切換到指定Id的thread
3. 堆疊資訊除錯
(gdb) bt full 顯示當前的堆疊資訊
4. 顯示當前執行的原始碼資訊
(gdb) ctrl + x + a (full) : 相當強大的功能,將會在當前視窗建立一個小的視窗用於顯示原始碼
(gdb) up/down: 依據呼叫堆疊顯示上一級/下一級呼叫的原始碼
4. 程式單步執行/繼續執行控制/退出執行
(gdb) c 執行程式直至斷點/觀察點處停住
(gdb) n 單步執行
(gdb) quit 退出執行
5. 斷點資訊除錯
(gdb) b(reak) filename:linenumber 執行到檔案指定行停住;不需要指定檔案的路徑
(gdb) b(reak) filename:function 執行到檔案指定函式入口停住;不需要指定檔案的路徑
6. 觀察點資訊除錯
(gdb) watch *(long *)0x7777777 觀察long型別變數,當變數變化時,程式停住
(gdb) rwatch *(long *)0x7777777 觀察long型別變數,當變數的值被讀時,程式停住
(gdb) awatch *(long *)0x7777777 設定觀察點,當變數的值被讀或寫時,程式停住
7. 改變執行環境除錯
(gdb) print x = 4 C/C++語法,把變數x的值修改為4
(gdb) jump +num 當前執行點向下偏移num行開始執行
(gdb) jump linenum 從當前除錯檔案的linenum行開始執行
(gdb) jump file:linenum 從file的linenum行開始執行
(gdb) singal SIGNAL 傳送訊號SINGAL給被除錯程式
(gdb) return 強制函式返回,忽略未執行的語句
(gdb) return result 強制函式返回結果result,忽略未執行的語句
(gdb) call func 呼叫當前程式中的函式