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
, wherepath
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 的输出目录
- adb shell mkdir
-
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)
如果以 SHARED 方式生成了 liballoc_hook.so 共享库,则需要将 liballoc_hook.so 文件放在可执行程序可读取的目录中,例如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; }
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_
: 抓取堆栈的最大深度,默认 128backtrace_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, 可在该文件中修改上述参数