为 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 已经编译出来,决定放到目标设备上跑跑看,一跑,成功了!
(六)运行
像上面那样 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 到设备上运行,根据出错信息找缺失的文件……不过还是相当值得,利器在手啦。