1. 程式人生 > >android應用空間的除錯方法

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));
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);
}
然後就是這條列印資訊,導致我開啟wifi的時候,出現如下典型的錯誤資訊:
上述資訊中,backtrace部分才是我們真正需要的資訊,他顯示了程式跑飛時的呼叫流程。 我在板子上跑的wpa_supplicant是stripe symbol的可執行程式,所有隻有二進位制, 沒有對應的標號資訊。不過android很體貼的為我們保留了一份帶symbos的wpa_supplcant可執行程式。 在android編譯輸出的如下路徑下: out/target/product/wmid/symbols/system/bin,通過file命令,你可以看到wpa_supplcant是 not stripped的。
然後通過addr2line工具將上面pc對應的16進位制值轉換成函式呼叫返回時所在檔案的行數, 從而可以確定函式的呼叫流程:
根據以上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的方法:
  1. $chmod 777 /data/anr

  2. rm /data/anr/traces.txt

  3. ps

  4. kill -3 PID

需要注意的是:pid是程序的id號,並且一般都是針對pid大於1000的程序。對於那些native的deamon,執行kill -3 $pid則不能生成trace.txt檔案 3: 看程式碼
根據棧回朔的資訊來檢視程式碼,仔細查看出問題點周圍的程式碼是否有問題。 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 引數值)