1. 程式人生 > >Android環境下的GDB除錯

Android環境下的GDB除錯

gdb是GNU開發的針對Linux/Unix環境下程式的除錯工具。為了節約目標系統的資源,gdb通常採用gdb+gdbserver的方式進行除錯。

在Android GDB除錯場景下,gdb執行在PC端,gdbserver執行在Android系統中。在實際的除錯過程中,PC端的gdb參照除錯符號檔案向gdbserver發出命令,gdbserver就會向執行程式發出訊號,從而實現對Android系統執行程式的跟蹤與控制:

  • 啟動尚未執行C/C++程式,或者繫結到正在執行的C/C++程式
  • 讓被除錯的程式在指定的斷點處中止執行
  • 檢視當前中止狀態下的對用堆疊,區域性變數等資訊
  • 通過對變數賦值,呼叫函式等操作來動態的改變程式的執行環境

除錯環境準備

  1. 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-gdb

  2. gdbserver
    gdbserver執行在Android系統中,預設已經安裝在eng或者userdebug的軟體版本中;根據被除錯程式的執行位數(32位/64位)的不同,gdbserver需要選擇相匹配的版本gdbserver/gdbserver64。
    注意:如果手機中找不到gdbserver,我們可以將原始碼prebuilt目錄下的gdbserver安裝到手機中

  3. 帶有除錯符號資訊的檔案
    要除錯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       呼叫當前程式中的函式