1.1 基本原理
使用bionc的libc初始时,会检测属性"libc.debug.malloc",
//android/bionic/libc/bionic/malloc_debug_common.c //static void malloc_init_impl(void) if (!debug_level && __system_property_get("libc.debug.malloc", env)) { debug_level = atoi(env); } /* Debug level 0 means that we should use dlxxx allocation * routines (default). */ if (!debug_level) { return; } // Lets see which .so must be loaded for the requested debug level switch (debug_level) { case 1: case 5: case 10: so_name = "/system/lib/libc_malloc_debug_leak.so"; break; case 20: // Quick check: debug level 20 can only be handled in emulator. if (!qemu_running) { error_log("%s: Debug level %d can only be set in emulator\n", __progname, debug_level); return; } // Make sure that memory checking has been enabled in emulator. if (!memcheck_enabled) { error_log("%s: Memory checking is not enabled in the emulator\n", __progname); return; } so_name = "/system/lib/libc_malloc_debug_qemu.so"; break; default: error_log("%s: Debug level %d is unknown\n", __progname, debug_level); return; }
如果属性"libc.debug.malloc"被设置为以下值时,会使用不同的动态库文件:/system/lib/libc_malloc_debug_leak.so或者/system/lib/libc_malloc_debug_qemu.so中的malloc/free的调试接口
1 perform leak detection 5 fill allocated memory to detect overruns 10 fill memory and add sentinels to detect overruns 20 use special instrumented malloc/free routines for the emulator
其中20为模拟器调试使用,主要使用1来调试内存泄漏,10来调试内存越界。 调试接口实现主要在: android/bionic/libc/bionic/malloc_debug_common.c
1.2 内存泄漏
对于内存泄漏,其原理是在每次malloc前,建立一个AllocationEntry,并插入到全局hash表(gHashTable)中;每次free时,从全局hash表中删除表项。 开发者可自行调用get_malloc_leak_info()全局函数来获取当前全局hash表的情况打印出来以获取memroy占用情况。
1.3 内存泄漏如何查看
如android/frameworks/base/media/libmediaplayerservice/MediaPlayerService.cpp在binder dump接口中调用get_malloc_leak_info/free_malloc_leak_info来获取当前全局表中的示例。
//android/frameworks/base/media/libmediaplayerservice/MediaPlayerService.cpp void memStatus(int fd, const Vector<String16>& args) { const size_t SIZE = 256; char buffer[SIZE]; MyString8 result; typedef struct { size_t size; size_t dups; intptr_t * backtrace; } AllocEntry; uint8_t *info = NULL; size_t overallSize = 0; size_t infoSize = 0; size_t totalMemory = 0; size_t backtraceSize = 0; get_malloc_leak_info(&info, &overallSize, &infoSize, &totalMemory, &backtraceSize); 。。。 。。。 free_malloc_leak_info(info); } status_t MediaPlayerService::dump(int fd, const Vector<String16>& args) { 。。。。。。 #if defined(__arm__) bool dumpMem = false; for (size_t i = 0; i < args.size(); i++) { if (args[i] == String16("-m")) { dumpMem = true; } } if (dumpMem) { memStatus(fd, args); } #endif 。。。。。。 }
先使能memory debug功能为leak检测: 在init.rc中加入以下代码后,重启板子:
#setprop libc.debug.malloc 1
可以使用以下命令来查看meida player service的内存使用情况
#dumpsys media.player -m
1.4 内存越界
对于内存越界,其原理是在每次malloc前,在返回给用户的内存前后,各建立一个16字节的越界监测区,并填充此区域为特殊字符。free时,检测此区域是否被改写过。并打印出相应调用栈。
1.5 内存泄漏如何查看
先使能memory debug功能为leak检测: 在init.rc中加入以下代码后,重启板子:
#setprop libc.debug.malloc 10
然后查看logcat中的信息,如果发生越界,便会assert,并且打印出调用栈。
I/DEBUG ( 648): *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** I/DEBUG ( 648): Build fingerprint: 'CSR/sirfsocv7_android/sirfsocv7:2.3.7/GINGERBREAD/eng.xy05.20120806.111522:eng/test-keys' I/DEBUG ( 648): pid: 880, tid: 880 >>> /system/bin/mediaserver <<< I/DEBUG ( 648): signal 11 (SIGSEGV), code 1 (SEGV_MAPERR), fault addr deadbaad I/DEBUG ( 648): r0 00000027 r1 deadbaad r2 a0000000 r3 00000000 I/DEBUG ( 648): r4 00000001 r5 00000000 r6 a92260e8 r7 be92e9fc I/DEBUG ( 648): r8 00000000 r9 00000000 10 00000000 fp 00000000 I/DEBUG ( 648): ip afd46668 sp be92e800 lr afd193f9 pc afd15ec4 cpsr 60000030 I/DEBUG ( 648): d0 00000000bd6bc8e3 d1 0000000000000000 I/DEBUG ( 648): d2 0000000000000000 d3 0000000000000000 I/DEBUG ( 648): d4 0000000000000000 d5 0000000000000000 I/DEBUG ( 648): d6 0000000000000000 d7 3f80000000000000 I/DEBUG ( 648): d8 0000000000000000 d9 0000000000000000 I/DEBUG ( 648): d10 0000000000000000 d11 0000000000000000 I/DEBUG ( 648): d12 0000000000000000 d13 0000000000000000 I/DEBUG ( 648): d14 0000000000000000 d15 0000000000000000 I/DEBUG ( 648): scr 00000000 I/DEBUG ( 648): I/DEBUG ( 648): #00 pc 00015ec4 /system/lib/libc.so I/DEBUG ( 648): #01 pc 0007858c /system/lib/gst/libglib-2.0.so.0.2400.1 I/DEBUG ( 648): #02 pc 00077ebc /system/lib/gst/libglib-2.0.so.0.2400.1 I/DEBUG ( 648): #03 pc 00078090 /system/lib/gst/libglib-2.0.so.0.2400.1 I/DEBUG ( 648): #04 pc 00076e10 /system/lib/gst/libglib-2.0.so.0.2400.1 I/DEBUG ( 648): #05 pc 000772a4 /system/lib/gst/libglib-2.0.so.0.2400.1 I/DEBUG ( 648): #06 pc 000775cc /system/lib/gst/libglib-2.0.so.0.2400.1 I/DEBUG ( 648): #07 pc 000776e0 /system/lib/gst/libglib-2.0.so.0.2400.1 I/DEBUG ( 648): #08 pc 00041264 /system/lib/gst/libglib-2.0.so.0.2400.1 I/DEBUG ( 648): #09 pc 00009bcc /system/lib/gst/libGSTPlayerImpFactory.so I/DEBUG ( 648): #10 pc 0000a908 /system/lib/gst/libGSTPlayerImpFactory.so I/DEBUG ( 648): #11 pc 0001c292 /system/lib/libmediaplayerservice.so I/DEBUG ( 648): #12 pc 00017b32 /system/lib/libmediaplayerservice.so I/DEBUG ( 648): #13 pc 00017bfc /system/lib/libmediaplayerservice.so I/DEBUG ( 648): #14 pc 000089ec /system/bin/mediaserver I/DEBUG ( 648): #15 pc 00014d72 /system/lib/libc.so I/DEBUG ( 648): I/DEBUG ( 648): code around pc: I/DEBUG ( 648): afd15ea4 2c006824 e028d1fb b13368db c064f8df I/DEBUG ( 648): afd15eb4 44fc2401 4000f8cc 49124798 25002027 I/DEBUG ( 648): afd15ec4 f7f57008 2106eb6c ecc8f7f6 460aa901 I/DEBUG ( 648): afd15ed4 f04f2006 95015380 95029303 e82ef7f6 I/DEBUG ( 648): afd15ee4 462aa905 f7f62002 f7f5e83a 2106eb58 I/DEBUG ( 648): I/DEBUG ( 648): code around lr: I/DEBUG ( 648): afd193d8 4a0e4b0d e92d447b 589c41f0 26004680 I/DEBUG ( 648): afd193e8 686768a5 f9b5e006 b113300c 47c04628 I/DEBUG ( 648): afd193f8 35544306 37fff117 6824d5f5 d1ef2c00 I/DEBUG ( 648): afd19408 e8bd4630 bf0081f0 00028124 ffffff88 I/DEBUG ( 648): afd19418 b086b570 f602fb01 9004460c a804a901 I/DEBUG ( 648): I/DEBUG ( 648): stack: I/DEBUG ( 648): be92e7c0 afd426b8 I/DEBUG ( 648): be92e7c4 00000015 I/DEBUG ( 648): be92e7c8 818f39cc /system/lib/gst/libglib-2.0.so.0.2400.1 I/DEBUG ( 648): be92e7cc afd191c5 /system/lib/libc.so I/DEBUG ( 648): be92e7d0 afd4270c I/DEBUG ( 648): be92e7d4 afd426b8 I/DEBUG ( 648): be92e7d8 00000000 I/DEBUG ( 648): be92e7dc afd193f9 /system/lib/libc.so I/DEBUG ( 648): be92e7e0 00000001 I/DEBUG ( 648): be92e7e4 be92e814 I/DEBUG ( 648): be92e7e8 a92260e8 I/DEBUG ( 648): be92e7ec be92e9fc I/DEBUG ( 648): be92e7f0 00000000 I/DEBUG ( 648): be92e7f4 afd1871b /system/lib/libc.so I/DEBUG ( 648): be92e7f8 df002777 I/DEBUG ( 648): be92e7fc e3a070ad I/DEBUG ( 648): #00 be92e800 0000000a I/DEBUG ( 648): be92e804 afd1ca1f /system/lib/libc.so I/DEBUG ( 648): be92e808 0000000a I/DEBUG ( 648): be92e80c afd426b8 I/DEBUG ( 648): be92e810 a92260e8 I/DEBUG ( 648): be92e814 fffffbdf I/DEBUG ( 648): be92e818 81933494 I/DEBUG ( 648): be92e81c 81933494 I/DEBUG ( 648): be92e820 818f39e4 /system/lib/gst/libglib-2.0.so.0.2400.1 I/DEBUG ( 648): be92e824 81878590 /system/lib/gst/libglib-2.0.so.0.2400.1 I/DEBUG ( 648): #01 be92e828 be92e844 I/DEBUG ( 648): be92e82c 00000000 I/DEBUG ( 648): be92e830 81933494 I/DEBUG ( 648): be92e834 81933494 I/DEBUG ( 648): be92e838 a9220d80 /system/lib/libmediaplayerservice.so I/DEBUG ( 648): be92e83c 81877ec0 /system/lib/gst/libglib-2.0.so.0.2400.1 I/DEBUG ( 648): be92e840 818f38d0 /system/lib/gst/libglib-2.0.so.0.2400.1 I/DEBUG ( 648): be92e844 818f3990 /system/lib/gst/libglib-2.0.so.0.2400.1 I/DEBUG ( 648): be92e848 0002b800 I/DEBUG ( 648): be92e84c 818f3990 /system/lib/gst/libglib-2.0.so.0.2400.1
但此时,仍然不能定位到底是哪段程序造成了此段地址越界(即不能定位到发生越界时的第一现场),我们需要更为强大的内存检测工具- valgrind来做到代码级定位。
2. 使用valgrind工具检测进程内存情况
valgrind已经在Android 4.0和4.1中默认支持。我已经backport此包到CSR prima2 2.3中,
使用方法:
To run Valgrind on the system server:
#setprop wrap.system_server "/system/bin/logwrapper /data/local/valgrind/bin/valgrind" #stop #start
To run Valgrind on an application:
#setprop wrap.<process_name> "/system/bin/logwrapper /data/local/valgrind/bin/valgrind"
Truncate the combined property name "wrap." + the process name to 31 chars if needed.
The system property server has a maximum length limit on property names.
Of course you can pass other arguments to valgrind or run other tools instead. You MUST have root for this to work.
需要注意的是: 由于valgrind的介入,从而使systemserver/app启动过程非常缓慢。 并且打印出类似以下的valgrind调试信息:
I//data/local/valgrind/bin/valgrind( 1042): ==1043== Thread 10: I//data/local/valgrind/bin/valgrind( 1042): ==1043== Conditional jump or move depends on uninitialised value(s) I//data/local/valgrind/bin/valgrind( 1042): ==1043== at 0x81135530: ??? (in /system/lib/egl/libGLESv1_CM_POWERVR_SGX531_110.so.1.1.16.4117) I//data/local/valgrind/bin/valgrind( 1042): ==1043== I//data/local/valgrind/bin/valgrind( 1042): ==1043== Conditional jump or move depends on uninitialised value(s) I//data/local/valgrind/bin/valgrind( 1042): ==1043== at 0x81135548: ??? (in /system/lib/egl/libGLESv1_CM_POWERVR_SGX531_110.so.1.1.16.4117) I//data/local/valgrind/bin/valgrind( 1042): ==1043== I//data/local/valgrind/bin/valgrind( 1042): ==1043== Conditional jump or move depends on uninitialised value(s) I//data/local/valgrind/bin/valgrind( 1042): ==1043== at 0x81135574: ??? (in /system/lib/egl/libGLESv1_CM_POWERVR_SGX531_110.so.1.1.16.4117) I//data/local/valgrind/bin/valgrind( 1042): ==1043== I//data/local/valgrind/bin/valgrind( 1042): ==1043== Conditional jump or move depends on uninitialised value(s) I//data/local/valgrind/bin/valgrind( 1042): ==1043== at 0x8113557C: ??? (in /system/lib/egl/libGLESv1_CM_POWERVR_SGX531_110.so.1.1.16.4117) I//data/local/valgrind/bin/valgrind( 1042): ==1043== I//data/local/valgrind/bin/valgrind( 1042): ==1043== Use of uninitialised value of size 4 I//data/local/valgrind/bin/valgrind( 1042): ==1043== at 0x81135590: ??? (in /system/lib/egl/libGLESv1_CM_POWERVR_SGX531_110.so.1.1.16.4117) I//data/local/valgrind/bin/valgrind( 1042): ==1043==
另外可使用带symbol的动态链接库文件进行调试。
Android Memory Usage :
http://elinux.org/Android_Memory_Usage
valgrind in android:
https://bugs.kde.org/show_bug.cgi?id=266035#c17
有用得连接介绍:
http://blog.csdn.net/sduliulun/article/details/7732906