如何在安卓系统中侦测和调试内存泄露和越界

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的调试接口

1perform leak detection
5fill allocated memory to detect overruns
10fill memory and add sentinels to detect overruns
20use 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


  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值