Android系统malloc_debug的使用

malloc_debug是Android中用于内存调试的工具,它能够跟踪内存分配、释放,支持内存边界检查、调用栈跟踪等功能。通过设置特定属性,如backtrace、leak_track等,可以在进程退出时检测内存泄漏或运行时检查内存错误。启用malloc_debug涉及修改系统属性,加载debug_malloc_shared_library并替换内存管理函数。此外,malloc_debug还可用于检测double free、use after free等常见内存问题,对于内存优化和调试具有重要作用。

一.malloc_debug简介
android 的libc中有malloc_debug的hook调用,具体android源码/bionic/libc/malloc_debug下,我们可以使用malloc_debug中的hook函数对内存分配进行跟踪加测。

malloc_debug主要包含的功能如下:
1.内存分配和释放跟踪,支持的函数如下:

When malloc debug is enabled, it works by adding a shim layer that replaces
the normal allocation calls. The replaced calls are:

* `malloc`
* `free`
* `calloc`
* `realloc`
* `posix_memalign`
* `memalign`
* `malloc_usable_size`

2 支持内存边界,可以在申请的内存头部和尾部添加guard,内存越界检查,use after free,内存崩溃检查等.
3 调用栈跟踪和打印,跟踪内存分配的同时保存内存分配的调用栈,方便内存泄漏检查.

二.malloc_debug的使用
1.stop
adb shell stop
2 开启malloc_debug
adb shell setprop libc.debug.malloc.program app_process
3 设置相关检测项
adb shell setprop libc.debug.malloc.options backtrace
4 start
adb shell start

三 malloc_debug原理
/bionic/libc/Android.bp
分析bp文件可以得知我们libc.so中包含了/bionic/libc/bionic/malloc_common.cpp

// ========================================================
// libc.a + libc.so
// ========================================================
cc_library {
    defaults: ["libc_defaults"],
    name: "libc",
    product_variables: {
        platform_sdk_version: {
            asflags: ["-DPLATFORM_SDK_VERSION=%d"],
        },
    },
    static: {
        srcs: [
            "bionic/dl_iterate_phdr_static.cpp",
            "bionic/icu_static.cpp",
            "bionic/malloc_common.cpp",
            "bionic/libc_init_static.cpp",
        ],
        cflags: ["-DLIBC_STATIC"],
        whole_static_libs: ["libc_init_static", "libjemalloc"],
    },
    shared: {
        srcs: [
            "arch-common/bionic/crtbegin_so.c",
            "arch-common/bionic/crtbrand.S",
            "bionic/icu.cpp",
            "bionic/malloc_common.cpp",
            "bionic/NetdClient.cpp",
            "arch-common/bionic/crtend_so.S",
        ],
        whole_static_libs: ["libc_init_dynamic"],
    },
......
}

libc在初始化时会调用malloc_init_impl判断属性来加载libc_malloc_debug.so,调用InitMallocFunctions替换掉Libc原生的内存分配和释放函数。

malloc_comon.cpp

static const char* DEBUG_SHARED_LIB = "libc_malloc_debug.so";
static const char* DEBUG_MALLOC_PROPERTY_OPTIONS = "libc.debug.malloc.options";
static const char* DEBUG_MALLOC_PROPERTY_PROGRAM = "libc.debug.malloc.program";

// __libc_init会调用__libc_init_malloc,进而调到malloc_init_impl进行初始化
// Initializes memory allocation framework.
// This routine is called from __libc_init routines in libc_init_dynamic.cpp.
__LIBC_HIDDEN__ void __libc_init_malloc(libc_globals* globals) {
  malloc_init_impl(globals);
}

// Initializes memory allocation framework once per process.
static void malloc_init_impl(libc_globals* globals) {
  char value[PROP_VALUE_MAX];

  // If DEBUG_MALLOC_ENV_OPTIONS is set then it overrides the system properties.
  const char* options = getenv(DEBUG_MALLOC_ENV_OPTIONS);
  if (options == nullptr || options[0] == '\0') {
  //判断libc.debug.malloc.options属性如果为空,则直接返回
    if (__system_property_get(DEBUG_MALLOC_PROPERTY_OPTIONS, value) == 0 || value[0] == '\0') {
      return;
    }
    options = value;

    // Check to see if only a specific program should have debug malloc enabled.
    char program[PROP_VALUE_MAX];
    //判断libc.debug.malloc.program不为空,并且program为null,此时也是直接返回。
    if (__system_property_get(DEBUG_MALLOC_PROPERTY_PROGRAM, program) != 0 &&
        strstr(getprogname(), program) == nullptr) {
      return;
    }
  }

  //如果进行了属性设置,则将利用libc_malloc_debug.so库中的部分函数替换libc.so的相关接口。
  // Load the debug malloc shared library.
  void* malloc_impl_handle = dlopen(DEBUG_SHARED_LIB, RTLD_NOW | RTLD_LOCAL);
  if (malloc_impl_handle == nullptr) {
    error_log("%s: Unable to open debug malloc shared library %s: %s",
              getprogname(), DEBUG_SHARED_LIB, dlerror());
    return;
  }

  // Initialize malloc debugging in the loaded module.
  auto init_func = reinterpret_cast<bool (*)(const MallocDispatch*, int*, const char*)>(
      dlsym(malloc_impl_handle, "debug_initialize"));
  if (init_func == nullptr) {
    error_log("%s: debug_initialize routine not found in %s", getprogname(), DEBUG_SHARED_LIB);
    dlclose(malloc_impl_handle);
    return;
  }

  // Get the syms for the external functions.
  void* finalize_sym = dlsym(malloc_impl_handle, "debug_finalize");
  if (finalize_sym == nullptr) {
    error_log("%s: debug_finalize routine not found in %s", getprogname(), DEBUG_SHARED_LIB);
    dlclose(malloc_impl_handle);
    return;
  }

  void* get_leak_info_sym = dlsym(malloc_impl_handle, "debug_get_malloc_leak_info");
  if (get_leak_info_sym == nullptr) {
    error_log("%s: debug_get_malloc_leak_info routine not found in %s", getprogname(),
              DEBUG_SHARED_LIB);
    dlclose(malloc_impl_handle);
    return;
  }
int __system_property_get(const char *name, char *value)
{
    const prop_info *pi = __system_property_find(name);
    if(pi != 0) {
        return __system_property_read(pi, 0, value);
    } else {
        value[0] = 0;
        return 0;
    }
}

四 malloc_debug option选项
1 内存边界检查
front_guard[=SIZE_BYTES]Enables a small buffer placed before the allocated data.
rear_guard[=SIZE_BYTES] Enables a small buffer placed after the allocated data.
guard[=SIZE_BYTES] Enables both a front guard and a rear guard on all allocations.
主要原理是在分配内存的头部和尾部添加一段数据,作为边界,头部初始化为0xaa,尾部初始化为0xbb。

2 调用栈功能
backtrace[=MAX_FRAMES]
backtrace_enable_on_signal[=MAX_FRAMES]
backtrace_dump_on_exit
backtrace_dump_prefix
backtrace_full
设置保存的调用栈个数,在信号量或者退出时打印调用栈

3 malloc内存默认值
fill_on_alloc[=MAX_FILLED_BYTES] size will be set to 0xeb.
fill_on_free[=MAX_FILLED_BYTES] When an allocation is freed, fill it with 0xef.
fill[=MAX_FILLED_BYTES] This enables both the fill_on_alloc option and the fill_on_free option.
expand_alloc[=EXPAND_BYTES] Add an extra amount to allocate for every allocation.

4 释放内存存档
free_track[=ALLOCATION_COUNT] 默认值是100,最大值是16384
free_track_backtrace_num_frames[=MAX_FRAMES]

5 分配释放检测
leak_track 在进程退出时,执行finalize函数,打印当前分配的内存
record_allocs[=TOTAL_ENTRIES] 记录alloc操作,The default value is 8,000,000 and the maximum value this can be set to is 50,000,000.
record_allocs_file[=FILE_NAME] 设置record_allocs保存地址
verify_pointers free/malloc_usable_size/realloc 有效检查
abort_on_error When malloc debug detects an error, abort after sending the error log message.内存泄漏检测不在此处,只在进程退出时检测

6 verbose 开启debug info log,如果要看更多的信息,建议开启此选项

五 常见用法
1 内存泄漏检测
在shell命令下执行 #setprop libc.debug.malloc.options “backtrace leak_track verbose”
这样开启后在进程退出时会打印leak信息,在发送kill -47时会打印当前内存申请

2 内存崩溃检查
在shell命令中添加guard #setprop libc.debug.malloc.options “backtrace leak_track verbose guard”
这样会检测内存覆盖等检测

3 verify_pointers 开启可以检测use after free和double free等操作

六.malloc问题
1.申请后多次释放 (double free)
2 释放后又去使用 (used after free)
3 使用越界 (比如申请了50节内存,结果在使用时多用了8字节的内存,这样就把后面的内存的内容踩坏,引起堆结构异常)
4 释放时传给free()的地址不是malloc()申请的地址,比如:p = malloc(10); free(p + 5);
5 内存泄露:申请内存后,忘记释放或某些代码路径没有释放。

七.实际运用
1.front_guard[=SIZE_BYTES]
每次调用malloc,都在分配的区域之前填充SIZE_BYTES,填充内容为0xaa
例子:setprop libc.debug.malloc.options front_guard=16

2.backtrace[=MAX_FRAMES]
MAX_FRAMES 最大值256 默认值16

每次在调用malloc时,malloc debug 都会记录malloc的调用栈(trace),栈的最大深度为MAX_FRAMES,MAX_FRAMES 越大,对malloc的性能影响越大。当进程收到信号(SIGRTMAX - 17)Android通常该信号值为47时,会触发malloc debug 的dump heap trace功能。默认dump路径在/data/local/tmp/ backtrace_heap.PID.txt。
给进程发信号通过kill -s 47 PID,进程收到信号后并不会马上dump backtrace,二是会等到下次调用malloc 或者 free时才会触发。所以如果发送信号后没有产生trace文件,请继续针对调试的进程做测试。

举例:
setprop libc.debug.malloc.options backtrace=5

3.backtrace_enable_on_signal[=MAX_FRAMES]
使能这个选项,通过给进程发送信号 45,可以动态开启和关闭backtrace 功能。

4.backtrace_dump_on_exit
进程退出后自动dump trace 文件,得到的dump文件为 $pid.exit.txt 结尾

5.backtrace_dump_prefix
trace dump的路径,如果需要放置其他目录,如/sdcard/heap, 则dump的文件路径为/sdcard/heap.$PID.txt

6.leak_track
程序结束后,如果有未free的指针,logcat中会打印出来

7.record_allocs[=TOTAL_ENTRIES],该选项很占内存,建议不开启
对进程中使用malloc, calloc, realloc 的地方进行记录,打印的格式如下
Threadid: action pointer size
471: malloc 0x72e00330c0 6
471: realloc 0x72e0005220 0x72e00330c0 12
471: free 0x72e012fcc0
471: free 0x72e0005220
471: malloc 0x72e01ade40 56
471: malloc 0x72e00330c0 6
注意,最大记录8,000,000 条

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值