為 Android 開發了一個 native 程式,使用 C 語言。測試時觀察記憶體占用,發現有記憶體洩漏。之前在 Linux 下使用過 valgrind ,于是想針對 Android 平台編譯一個 Valgrind 版本,用來檢測我的 native 程式。
我的開發環境是 Windows 7,決定使用 Cygwin 來編譯。
Cygwin 的具體安裝不多說了,注意的是,一些開發工具必須選擇,比如 gcc 、 autoconf 、 automake 等等。
Valgrind 最新的 release 版本是 3.9.0 ,官網下載下傳。下載下傳後解壓,閱讀源碼根目錄下的 README.android 檔案,先有個初步了解。
使用 Cygwin 來編譯 Valgrind 和在 Linux 環境下編譯還是有一些差别。我已經編譯通過并正常使用。首先進入 Cygwin 的 shell ,然後,下面是詳細的步驟。
(一)設定 NDK 路徑
指令很簡單,export NDKROOT=E:/android-ndk-r8d (替換為你開發主機上的實際路徑)。
需要注意的是,這裡的 NDKROOT 不能用 Cygwin 挂載的路徑 /cygdrive/e/xxx 這種形式,否則 configure 時檢測工具鍊是否可以生成可執行檔案時會找不到 ctrbegin_dynamic.o 及 C 庫,導緻 configure 失敗。這個花費了我将近一個小時的時間!
(二)設定硬體型号
export HWKIND=generic
我設定了上面的類型,實際應用中可以和具體的目标裝置關聯起來。
(三)設定工具鍊
這裡和 README.android 稍有不同。具體如下:
export AR=$NDKROOT/toolchains/arm-linux-androideabi-4.4.3/prebuilt/windows/bin/arm-linux-androideabi-ar.exe
export LD=$NDKROOT/toolchains/arm-linux-androideabi-4.4.3/prebuilt/windows/bin/arm-linux-androideabi-ld.exe
export CC=$NDKROOT/toolchains/arm-linux-androideabi-4.4.3/prebuilt/windows/bin/arm-linux-androideabi-gcc.exe
(四)配置( configure )
先進入到 valgrind 源碼根目錄。
找到 configure 檔案,做一些修改,否則 configure 會失敗。因為 configure 會檢測編譯主機核心版本,Cygwin 的 uname -r 傳回的版本不是 Linux 核心版本号,我的環境傳回是 1.7.28(0.271/5/3) 。找到 5508 行,修改成下面的樣子(在2.6.*前加入了1.7.*):
case "${kernel}" in
1.7.*|2.6.*|3.*)
這樣核心版本檢測才會通過。
配置選項還要做一些修改,這裡和 README.android 稍有不同,我添加 LIBS 選項,修改了 --target 。具體如下:
CPPFLAGS="--sysroot=$NDKROOT/platforms/android-14/arch-arm -DANDROID_HARDWARE_$HWKIND" \
CFLAGS="--sysroot=$NDKROOT/platforms/android-14/arch-arm" \
LIBS="-L$NDKROOT/platforms/android-14/arch-arm/usr/lib" \
./configure --prefix=/data/local/Inst --host=armv7-unknown-linux \
--target=arm-linux-androideabi --with-tmpdir=/sdcard
(五) 編譯
執行 make ,就這麼簡單了,我這裡通過了。
(六)安裝
make install ,這裡會失敗。我的錯誤資訊如下:
make[2]: Entering directory '/cygdrive/e/sources/valgrind-3.9.0/VEX'
priv/.deps/libvex_arm_linux_a-host_s390_defs.Po:1: *** 多個目标比對。 停止。
make[2]: Leaving directory '/cygdrive/e/sources/valgrind-3.9.0/VEX'
Makefile:665: recipe for target 'install-recursive' failed
make[1]: *** [install-recursive] Error 1
make[1]: Leaving directory '/cygdrive/e/sources/valgrind-3.9.0'
Makefile:990: recipe for target 'install' failed
make: *** [install] Error 2
我沒有搭理它,因為我發現 valgrind 和 memcheck 已經編譯出來,決定放到目标裝置上跑跑看,一跑,成功了!
實際上為了做記憶體檢測,需要 valgrind 、memcheck 、default.supp 、 vgpreload_core-arm-linux.so 、vgpreload_memcheck-arm-linux.so 這五個檔案, push 到裝置上即可。
(六)運作
像上面那樣 push 程式到裝置上,要想運作,必須設定一個環境變量 VALGRIND_LIB ,valgrind 會根據這個環境變量來查找要加載的工具,如 memcheck 等,否則會報錯,找不到檔案。
現在可以這麼啟動目标程式了:
/data/data/valgrind/valgrind --leak-check=full --track-origins=yes --log-file=check.log your-target-executable
我們把日志輸出到 check.log 檔案中,友善檢視。
------------
好啦,到現在為止, valgrind for android 已經可以使用了。
整個過程花了我 2 個多小時,對着 Cygwin 下面的報錯,檢視 config.log ,修改指令,修改 configure ,push 到裝置上運作,根據出錯資訊找缺失的檔案……不過還是相當值得,利器在手啦。