Skip to content

一个比较完善的内存分析工具,通过 unwind 的方式获取堆栈,并 hook 了 malloc、free、realloc、calloc、ioctl、close、mmap、munmap 等内存调用相关的 API,用于记录内存申请的堆栈信息,可用于记录内存峰值,检查内存泄露,优化内存等,未来打算使用火焰图的方式展示内存生命周期

Notifications You must be signed in to change notification settings

Gao-HaoYuan/dmabuffer_alloc_trace

 
 

Repository files navigation

liballoc_hook.so

use to get malloc and free backtrace, include dmabuffer by hook ioctl and close

  • how to build

    • ./build_android.sh [armeabi-v7a]
  • how to use

    • adb shell mkdir path, where path is the output path for the unwindstack, default: /data/local/tmp/trace/backtrace_heap
    • LD_PRELOAD=liballoc_hook.so LD_LIBRARY_PATH=. ls
    • then replace ls to you real command
    • 如果要抓 trace 请先创建 trace 的输出目录
  • checkpoint

    • 支持在程序指定位置插入检查点,输出当前时刻的未释放的内存的堆栈信息
    • 第一种方式:使用信号的方式触发堆栈输出,默认信号值为 33,可以在上述配置文件 Config.cpp 中修改,trace 文件以当前时间命名
        #include <unistd.h>
        #include <signal.h>
        #include <errno.h>
        #include <stdlib.h>
        #include <string.h>
      
        if (kill(getpid(), 33) == -1) {
          fprintf(stderr, "Error in file %s at line %d: %s\n", __FILE__, __LINE__, strerror(errno));
          exit(1);
        }
    • 第二种方式:在代码中插入调用,支持用户自定义文件名,前提需要用户的可执行程序依赖该项目编译生成的 so, 例如在 cmake 文件中做出下面的修改
        add_library(hook SHARED IMPORTED)
        set(HOOK_IMPORTED_LOCATION "${CMAKE_CURRENT_SOURCE_DIR}/liballoc_hook.so")
        set_target_properties(hook PROPERTIES IMPORTED_LOCATION "${HOOK_IMPORTED_LOCATION}")
      
        add_executable(xxxx ./code/xxxxx.cpp)
        target_link_libraries(xxxx hook)
      
      用户代码需做以下修改
        extern "C" void checkpoint(const char* file_name);
        int main() {
            size_t size = 200 * 1024 * 1024;
            void* addr_mmap = mmap(nullptr, size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS | MAP_POPULATE, -1, 0);
            checkpoint("/data/local/tmp/trace/check_point.1.txt");
            void* addr_malloc = malloc(size * 2);
            
            return 0;
        }
      如果以 SHARED 方式生成了 liballoc_hook.so 共享库,则需要将 liballoc_hook.so 文件放在可执行程序可读取的目录中,例如
        adb push /path/liballoc_hook.so ${test_dir}
      
        adb exec-out "cd ${test_dir}; \
                  export LD_LIBRARY_PATH=.; \
                  ./xxxx;"
  • 如何改造自己的被测试程序以便此工具能有效采样

    另外在采样过程中,也请务必保证程序处于停止状态,常见的做法是在被测试的代码适当位置加上 checkpoint() 或者 kill(getpid(), 33) 以便触发采样, 下面解释一下什么叫做适当位置

    比如典型的 pipline:
    create_ctx()
    ....
    use_ctx()
    ....
    free_ctx()
    

    一般期望是 free_ctx 后应该资源都释放了(除开常驻的外), 我们改造上述 pipline 来加入 checkpoint()/kill, 以便让此工具有采样点

    for(;;)
    {
    	create_ctx()
    	....
    	use_ctx()
    	....
    	free_ctx()
    	checkpoint() or kill(getpid(), 33)
    }
    

    典型的,我们需要让整个 pipline 运行几次分别得到不同运行次数的采样 trace, 当然除了 checkpoint/kill 让程序暂停外,我们也可以通过 gdb 等调试工具来达到相同的目的。关键在于加的位置,一定是你认为这个点应该释放了资源,比如典型的上面的 free_ctx,一定不要在其他点去进行采样,比如 use_ctx 阶段,这个时候本身属于内存用量高峰,即使没有释放也不能说明泄漏对吧。

  • 抓取峰值步骤

    • 首先运行一次程序,当程序结束时,会输出如下信息
    +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
    host peak used: 415MB, dma peak used 206MB, total peak used: 619MB
    
    • 然后,根据 total peak used 的值,通过环境变量 DUMP_PEAK_VALUE_MB 设置 backtrace_dump_peak_val_ 的值,通常要小于 total peak used 50MB 左右,设置方式如下
    export DUMP_PEAK_VALUE_MB=xxx
    
    或者
    
    DUMP_PEAK_VALUE_MB=xxx LD_PRELOAD=liballoc_hook.so LD_LIBRARY_PATH=. ls
    
    • DUMP_PEAK_VALUE_MB 的单位默认为 MB
  • 抓取峰值步骤

    • 利用 cheakpoint 机制执行两次程序,并对两次的内存调用堆栈输出进行对比,分析内存调用的增量,此时的内存调用是以时间排序,可以从后向前对比
      void test() {
        ....
      }
    
      int main() {
         for (int i = 0; i < 2; ++i) {
          test();
          kill(getpid(), 33);
         }
      }
    
  • 配置参数意义

    • backtrace_dump_on_exit_: 程序退出时,打印堆栈
    • backtrace_frames_: 抓取堆栈的最大深度,默认 128
    • backtrace_dump_prefix_: 输出堆栈信息的文件名前缀
    • BACKTRACE_SPECIFIC_SIZES: 分配内存时,是否抓取指定 alloc size 的堆栈信息
    • backtrace_min_size_bytes_: 开启 BACKTRACE_SPECIFIC_SIZES 标志时,抓取 alloc size 大于该值的堆栈信息
    • backtrace_max_size_bytes_: 开启 BACKTRACE_SPECIFIC_SIZES 标志时,抓取 alloc size 小于该值的堆栈信息
    • RECORD_MEMORY_PEAK: 是否抓取峰值时刻的堆栈信息
    • backtrace_dump_peak_val_: 当峰值大于该值时,记录峰值时刻的堆栈
    • DUMP_ON_SINGAL: 开启 checkpoint 信号机制
    • backtrace_dump_signal_: checkpoint 信号机制的信号值,默认 33
    • 配置文件位于 backtrace/src/Config.cpp, 可在该文件中修改上述参数

About

一个比较完善的内存分析工具,通过 unwind 的方式获取堆栈,并 hook 了 malloc、free、realloc、calloc、ioctl、close、mmap、munmap 等内存调用相关的 API,用于记录内存申请的堆栈信息,可用于记录内存峰值,检查内存泄露,优化内存等,未来打算使用火焰图的方式展示内存生命周期

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages

  • C++ 94.4%
  • C 3.2%
  • Assembly 1.4%
  • Other 1.0%