android應用空間的除錯方法
下面都是android應用層空間的除錯方法總結
1 android native 和後臺服務的除錯方法
android系統中除錯Java非常容易,一般遇到錯誤都在logcat中打印出錯時函式的呼叫關係,和出錯的具體行數。而C庫或是可執行的後臺服務中出錯時只看到一些二進位制資訊。
下面我就以實際的列子來說明這種情況除錯方法和步驟:我在跟蹤wpa_supplicant的問題時,我在wpa_supplicant_8/wpa_supplicant/bss.c檔案的wpa_bss_copy_res函式中,添加了了一條列印資訊:wpa_dbg(dst,MSG_ERROR, "---------------src->tsf:%llu ||"MACSTR "\n", src->tsf,MAC2STR(src->bssid));
然後就是這條列印資訊,導致我開啟wifi的時候,出現如下典型的錯誤資訊:static void wpa_bss_copy_res(struct wpa_bss *dst, struct wpa_scan_res *src, struct os_time *fetch_time) { dst->flags = src->flags; os_memcpy(dst->bssid, src->bssid, ETH_ALEN); dst->freq = src->freq; dst->beacon_int = src->beacon_int; dst->caps = src->caps; dst->qual = src->qual; dst->noise = src->noise; dst->level = src->level; dst->tsf = src->tsf; wpa_dbg(dst,MSG_ERROR, "---------------src->tsf:%llu ||"MACSTR "\n", src->tsf,MAC2STR(src->bssid));//是我新增的除錯log calculate_update_time(fetch_time, src->age, &dst->last_update); }
上述資訊中,backtrace部分才是我們真正需要的資訊,他顯示了程式跑飛時的呼叫流程。 我在板子上跑的wpa_supplicant是stripe symbol的可執行程式,所有隻有二進位制, 沒有對應的標號資訊。不過android很體貼的為我們保留了一份帶symbos的wpa_supplcant可執行程式。 在android編譯輸出的如下路徑下: out/target/product/wmid/symbols/system/bin,通過file命令,你可以看到wpa_supplcant是 not stripped的。
根據以上addr2line得到的函式呼叫的回朔資訊,我們結合原始碼可以得出,出問題時,函式 的呼叫流程如下:
process_global_event
|-->do_process_drv_event
|-->send_scan_event
|-->wpa_supplicant_event
|-->wpa_supplicant_event_scan_results
|-->_wpa_supplicant_event_scan_results
|-->wpa_supplicant_get_scan_results
|-->wpa_bss_update_scan_res
|-->wpa_bss_add
wpa_bss_copy_res
wpa_dbg
||
wpa_msg
wpa_msg_cb
||
wpa_supplicant_ctrl_iface_msg_cb
即出問題時,正是我們在wpa_bss_copy_res函式中,新增的wpa_dbg函式引起的。原因是 我故意給wpa_dbg函式傳遞了一個非struct wpa_supplicant結構體的指標值,導致後面在 wpa_supplicant_ctrl_iface_msg_cb函式中,把這個指標當作struct wpa_supplicant結構來引用, 所以出現地址錯誤。
2 gdb除錯
2.1:gdb遠端除錯的方法
gdb遠端除錯功能需要在linux-x86的環境下才能執行。譬如在ubuntu或debia環境下或
是linux的虛擬機器下,除錯步驟如下:
2.1.1: fetch adb path
首先獲取linux pc端下的adb的路徑,或者將adb的路徑設定到~/.bashrc下:
export PATH=$ADB_PATH:$PATH預設android fs編譯出來時在如下的路徑:
out/host/`uname -s | tr "[[:upper:]]" "[[:lower:]]"`-x86/bin/adb
2.1.2: fetch gdb path and python lib path
需要獲取gdb的client端和python 庫的路徑(gdb需要依賴python),android4.4以後,
一般都是在如下路徑:GDB=prebuilts/gcc/$(uname -s | tr "[[:upper:]]" "[[:lower:]]")-x86
/arm/arm-linux-androideabi-4.7/bin/arm-linux-androideabi-gdb
PYTHON_DIR=prebuilts/python/$(uname -s | tr "[[:upper:]]" "[[:lower:]]")-x86/2.7.5
接下來我們需要將pc 端上的gdb(簡稱為host)跟目標版上的gdb service(簡稱為target)
連線起來,讓他們可以相互通訊
2.1.3: connect gdb client and gdb service
$ADB forward tcp:$GDB_PORT tcp:$GDB_PORT
在這裡需要規定下gdb service的偵聽埠和gdb client的連線埠,通過adb forward來實現,
這樣client與service就可以通過adb來通訊了。至於adb forward具體幹些什麼事情,
2.1.4: start gdbserver on the target with attached target prog, and listening on $GDB_PORT
接下來需要在目標版上起動gdb server,在指定的埠上偵聽gdb client過來的請求,並且attach
上需要除錯的程序。使用如下命令:$ADB shell gdbserver :$GDB_PORT --attach $TARGET_PID &
2.1.5:設定pc端下gdb的庫和可執行程式的搜尋路徑,
注意這些庫和可執行程式,是包含了除錯資訊的。在android編譯輸出的如下路徑:
out/target/product/wmid/symbols/system,而目標版上的二進位制程式則是不包含除錯資訊的。
遠端除錯的好處就在這裡,簡單說,就是將包含除錯資訊的程式放在pc端,target端的程式是
不包含除錯資訊的(通過stripped命令去掉了除錯資訊),這樣節省儲存空間,gdb client會將
從gdb service那邊得到的二進位制資訊結合pc端的帶除錯資訊的程式,就可以翻譯成human-readable
的資訊展示出來並且幸運的是,android的編譯環境都為我們想好了這些,所以在android編譯的輸出路徑上,
都儲存了兩份,一份是帶除錯資訊,一份是不帶除錯資訊的。
2.1.6: start gdb client and execute $GDBINIT script
起動pc段的gdb,並同時指定要除錯的程式名字和路徑。
$GDB -x $GDBINIT $PROG
PROG就是要除錯的程式,注意他們pc端下的路徑,是包含除錯資訊的程式。
至此gdb除錯環境就已經搭建完成。以上過程,可以通過一個指令碼來實現,每次除錯時,
只需要執行以下這個指令碼,即可以開始gdb的除錯。執行該指令碼的方法:
將run-gdb.sh置於android fs sources的root 目錄下。然後執行如下命令:
run-gdb.sh attach prog
上面prog就是你需要除錯的程式,譬如如果要通過gdb除錯wpa_supplicant,則可以執行如下命令:
run-gdb.sh attach wpa_supplicant
2.2:gdb除錯死鎖的方法
在gdb的提示符下,輸入命令:thread apply all bt,即可以顯示所除錯程序下所有執行緒各
自的呼叫堆疊。或者在提示符下輸入:info threads,即可以檢視當前程序下所有的執行緒,
並且可以thread + number(簡寫:b n)命令來切換到指定ID(即number)的執行緒,然後執行
bt(backstrace)即可以列印當前執行緒下的呼叫堆疊。然後仔細檢視那些停在獲取鎖的地方的執行緒,
然後檢視這些執行緒都各自擁有那些鎖,找出導致死鎖的執行緒。
2.3:gdb常用命令
info threads
thread apply all bt
bt
print expr(p)
break
info break
run(r)
continue(c)
next(n)
quit(q)
3 android下特有的除錯命令debuggerd(/system/bin/debuggerd)
在android下有個/system/bin/debuggerd後臺服務,就是被android用來在需要的使用列印線
程的掉喲你堆疊的。這個debuggerd命令,在我們平時除錯andoid的死鎖程式也是很有用的。
應該多主動使用。基本流程就是先通過ps命令檢視你要除錯的程序的pid,然後執行命令:
debuggerd -b $pid,即可以將該$pid下所有的執行緒的堆疊都打印出來,類似於gdb中的
thread apply all bt命令,但是這個命令更方便。
4 ANR除錯
ANR is short forApplication No Response,一般主執行緒超過5秒未有處理就會ANR 1:首先分析log從LOG可以看出ANR的型別,CPU的使用情況:
如果CPU使用量接近100%,說明當前裝置很忙,有可能是CPU飢餓導致了ANR
如果CPU使用量很少,說明主執行緒被BLOCK了
如果IOwait很高,說明ANR有可能是主執行緒在進行I/O操作造成的
2: 從trace.txt檔案檢視呼叫stack.
手動獲取trace的方法:
-
$chmod 777 /data/anr
-
rm /data/anr/traces.txt
-
ps
-
kill -3 PID
根據棧回朔的資訊來檢視程式碼,仔細查看出問題點周圍的程式碼是否有問題。 4:仔細檢視ANR的成因(iowait?block?memoryleak?)
通過檢視anr,還可以發現執行緒的死鎖問題。
5 service和dumpsys命令的使用
service list命令可以列出目前android系統所有的服務程序(注意都是binder的服務程序)。
而dumpsys $service,則可以列印指定服務裡的一些重要資訊,這樣就可以動態的檢視
service裡面目前的狀態,從而發現是否有問題。
另外通過service call命令在控制檯就可以有選擇的呼叫你指定的服務中的指定api函式的功能;從而驗證該api是否工作正常
例如:
service call WMTWifiService 3 i32 0 i32 17
命令解析如下:
service call WMTWifiService(服務的名字,binder的知名服務,即註冊到serviceManager中的名字) 3(binder服務介面中的方法對應的編號) i32 0(引數1 引數值) i32 17(引數2 引數值)